In nội dung với định dạng riêng bằng UIPrintInteractionController

132

Ngày càng nhiều ứng dụng được bổ sung chức năng in ấn và đây là bài viết chuyên sâu bởi tác giả Nate Cook hướng dẫn sử dụng API UIPrintInteractionController để in ấn với nội dung chính xác như mong muốn.

Trong bài viết này tác giả sẽ hướng dẫn:

  • Làm thế nào để thiết lập công việc in ấn cơ bản bằng cách sử dụng UIPrintInfo, in với nội dung đã được định dạng với UIPrintItem.
  • In với các mục đã được định dạng với printItem, tạo UIPrintFormatter cơ bản và định dạng nội dung của bạn.
  • Sử dụng UIPageRenderer để in một mục với toàn quyền kiểm soát.
  • Thiết lập UIPrintPickerController để hiển thị một giao diện in ấn bên trong ứng dụng của iOS 8.

Từ Xcode 6, the printer simulator (máy in ảo hoá) được download từ Hardware IO Tools for Xcode. Với thiết bị ảo này giúp cho chúng ta test ứng dụng có chức năng in ấn (giả lập máy in thật).

Trọng tâm của UIKit Printing APIs là UIPrintInteractionController. Một ví dụ chung của lớp này là quản lý chi tiết các công việc in ấn và cấu hình bất kỳ UI nào mà được hiển thị cho người dùng. Nó cũng cung cấp 3 mức độ để điều khiển định dạng cho nội dung của bạn.

Printing is a Job

Trước khi chúng ta nhìn vào định dạng nội dung thật sự cho việc in ấn, chúng ta hãy đi qua những lựa chọn để cấu hình cho việc in ấn và những tuỳ chọn in hiển thị cho người dùng.

UIPrintInfo

Chi tiết của việc in ấn được thiết lập trong UIPrintInfo. Bạn có thể sử dụng những thuộc tính sau đây:

  • jobName String: tên của việc in ấn này. Cái tên này sẽ được hiển thị trong Print Center trên thiết bị và đối với một số máy in, nó sẽ hiển thị trên màn hình LCD.
  • orientation UIPrintInfoOrientation.Portrait (mặc định) hoặc .Landscape— điều này được bỏ qua nếu những gì bạn muốn in đã được định dạng bên trong, chẳng hạn như mội file PDF.
  • duplex UIPrintInfoDuplex.None.ShortEdge hoặc .LongEdge. Các thiết lập short-edge và long-edge cho biết cách 2 mặt của trang giấy có thể bị ràng buộc, trong khi đó .None ngăn chặn việc in 2 mặt (mặc dù không phải dùng UI để chuyển đổi cho in 2 mặt, gây khó hiểu)
  • outputType UIPrintInfoOutputType: Cung cấp cho UIKit một gợi ý về các kiểu mà nội dung bạn muốn in. Nó có thể là:
    • .General (mặc định): đối với văn bản và hình ảnh hỗn hợp; cho phép in 2 mặt.
    • .Grayscale: có thể là lựa chọn tốt hơn .General nếu nội dung của bạn chỉ có chữ màu đen.
    • .Photo: cho hình ảnh có màu hoặc ảnh trắng đen, vô hiệu hoá việc in 2 mặt và tiện lợi cho photo media với các loại giấy.
    • .PhotoGrayscale: có thể là lựa chọn tốt hơn .Photo cho hình ảnh chỉ có màu xám, phụ thuộc vào máy in.
  •  printerID String?: một ID cho một máy in cụ thể – bạn có thể nhận lấy ID này chỉ sau khi người dùng đã chọn máy in thông qua UI và lưu nó để dùng cho việc in ấn lần tới.

Ngoài ra, UIPrintInfo còn có thuộc tính dictionaryRepresentation để có thể lưu trữ và sử dụng để tạo ra một UIPrintInfo mới cho lần sau.

UIPrintInteractionController Settings

Có một số ít các thiết lập cho UIPrintInteractionController mà bạn có thể cấu hình trước khi hiển thị ra giao diện in ấn. Nó bao gồm:

  • printInfo UIPrintInfo: cấu hình của việc in ấn đã được nói đến ở trên.
  • printPaper UIPrintPaper: một kiểu đơn giản mô tả các kích thước vật lý và kích thước có thể in của các loại giấy; ngoại trừ cho những ứng dụng chuyên biệt, nó sẽ được xử lý giúp bạn bằng UIKit.
  • showsNumberOfCopies Bool: khi giá trị là true, người dùng có thể chọn số lượng copy.
  • showsPageRange Bool: khi giá trị là true, người dùng được phép chọn subrange từ nguyên liệu in ấn. Điều này chỉ có ích với nội dung nhiều trang – mặc định nó sẽ tắt cho hình ảnh.
  • showsPaperSelectionForLoadedPapers Bool: khi giá trị là true và máy in đã chọn có nhiều loại giấy, giao diện sẽ để cho người dùng chọn loại giấy để in.

Formatting Your Content

Mặc dù UIPrintInteractionController có 4 thuộc tính khác nhau, bạn có thể chọn mức độ điều khiển bạn muốn cho nội dung của bạn.

  • printingItem AnyObject! or printingItems [AnyObject]!: ở mức độ gần như cơ bản nhất, bộ điều khiển đơn giản chỉ là nhận nội dung mà có thể in được (hình ảnh và file PDF) và đưa chung tới máy in.
  • printFormatter UIPrintFormatter: ở mức độ tiếp theo, bạn có thể sử dụng subclass UIPrintFormatter để định dạng cho nội dung bên trong ứng dụng của bạn, sau đó đưa bộ định dạng tới UIPrintInteractionController. Bạn có một số kiểm soát định dạng và API in ấn phần lớn sẽ lo việc còn lại.
  • printPageRenderer UIPrintPageRenderer: ở mức độ cao nhất, bạn có thể tạo một custom subclass UIPrintPageRenderer, kết hợp định dạng các trang và lưu những cái mà bạn hay dùng cho headers, footers và nội dung trong trang.

Printing With printItem(s)

Bạn có thể in những nội dung có sẵn bằng cách thiết lập thuộc tính printItem hoặc là printItems của UIPrintInteractionController. Hình ảnh và file PDF có thể được đưa dưới dạng image data (NSData, UIImage hoặc ALAsset) hoặc bất kỳ một cái gì đó tham chiếu NSURL mà có thể được load vào đối tượng NSData. Để in được, những hình ảnh phải là một định dạng mà UIImage hỗ trợ.

Hãy xem qua một trường hợp rất đơn giản: hiển thị giao diện để in một tấm hình khi người dùng nhấn vào button. (Chúng ta sẽ thấy những cách xen kẽ nhau để khởi tạo việc in ấn dưới đây) Quá trình này phần lớn sẽ giống nhau, không vấn đề gì khi bạn đang in – cấu hình thông tin in ấn, thiết lập bộ điều khiển tương tác in ấn và cung cấp nội dung của bạn trước khi hiển thị giao diện:

Hàm presentAnimated(:completionHandler:) sử dụng cho việc hiển thị giao diện in ấn cho Iphone. Nếu in từ Ipad, sử dụng một trong hai hàm sau để thay thế: presentFromBarButtonItem(:animated:completonHandler:) hoặc presentFromRect(:inView:animated:completionHandler:)

UIPrintFormatter

Class UIPrintFormatter có hai subclass có thể dùng để định dạng văn bản (UISimpleTextPrintFormatter và UIMarkupTextPrintFormatter) cộng thêm (UIViewPrintFormatter) để định dạng nội dung của 3 view sau: UITextViewUIWebView và MKMapView. Trình định dạng in có một vài thuộc tính cho phép bạn xác định khu vực được in của trang bằng nhiều cách khác nhau; khu vực được in cuối cùng cho trình định dạng sẽ hình chữ nhật nhỏ nhất, đáp ứng các tiêu chí sau:

  • contentInsets UIEdgeInsets: một tập hợp các inset từ cạnh của trang giấy cho toàn bộ khối nội dung. Inset trái và phải được áp dụng cho tất cả các trang nhưng inset top chỉ được áp dụng cho trang đầu tiên. Inset bottom được bỏ qua.
  • perPageContentInsets UIEdgeInsets (chỉ có trong iOS 8): một tập hợp các inset từ cạnh của các trang giấy trong nội dung đã định dạng.
  • maximumContentWidth and maximumContentHeight CGFloat: nếu được chỉ định rõ, chúng có thể tiếp tục giới hạn chiều rộng và chiều cao của phạm vi nội dung.

Mặc dù tài liệu của Appe không nói rõ nhưng tất cả giá trị này dựa trên 72 point trong mỗi inch.

Có hai trình định dạng in dựa trên văn bản mà khi chúng được khởi tạo với text, chúng sẽ được định dạng. UISimpleTextPrintFormatter có thể xử lý văn bản đơn giản hoặc phức tạp, trong khi UIMarkupTextPrintFormatter nhận và render văn bản HTML trong thuộc tính markupText của nó. Hãy thử đưa một phiên bản HTML vào:

Kết quả:

Mặt khác, để sử dụng một UIViewPrintFormatter, bạn lấy một cái từ view bạn muốn in thông qua thuộc tính viewPrintFormatter của nó. Dưới đây là cách mà trình định dạng làm công việc của nó cho mỗi view mà nó hỗ trợ:

1) UITextView:

2) UIWebView

Print with UIWebView

3) MKMapView

Print with MKMapView

UIPrintPageRenderer

Trình định dạng được tích hợp vậy là ổn nhưng đối với công việc điều khiển tối đa trên trang đã in, bạn có thể tạo một subclass của  UIPrintPageRenderer. Trong subclass này bạn có thể kết hợp trình định dạng in ấn chúng ta đã thấy ở trên với những tuỳ biến riêng hay dùng để tạo các layout đẹp cho nội dung trong ứng dụng của bạn. Hãy xem một cách nữa để in ấn, lần này sẽ sử dụng trình render trang để thêm header và vẽ các hình ảnh dọc theo đoạn văn bản.

Trong lúc khởi tạo nó, chúng ta lưu trữ dữ liệu mà chúng ta sẽ cần để in, sau đó thiết lập headerHeight (hàm tạo header và footer thậm chí sẽ không được gọi trừ khi bạn thiết lập chiều cao tương ứng cho chúng) và tạo một markup text formatter cho đoạn văn bản.

Khi bạn sử dụng một hoặc nhiều trình định dạng in ấn như phần tuỳ biến render của bạn (như chúng ta đang làm ở đây), UIKit truy vấn chúng cho số lượng các trang để in. Nếu bạn đang tuỳ biến layout của trang đúng thì hàm numberOfPages() sẽ thực hiện để cung cấp những giá trị chính xác.

Tiếp theo, chúng ta sẽ override drawHeaderForPageAtIndex(:inRect:) để tạo những header tuỳ chỉnh. Thật không may, các tiện ích cho inset nội dung của mỗi trang trong trình định dạng được đưa đến đây, vì vậy điều đầu tiên chúng ta cần phải đưa tham số headerRect để phù hợp với các biên lề trang giấy, sau đó chỉ cần tạo dựng bên trong bối cảnh đồ hoạ hiện tại. Có một hàm drawFooterForPageAtIndex(:inRect:) để tạo footer:

Cuối cùng, hãy gọi hàm drawContentForPageAtIndex(:inRect:):

Với việc thực hiện các render hoàn tất trang tuỳ biến của chúng ta, chúng ta có thể thiết lập một trường hợp của thuộc tính pageRenderer  trong bộ điều khiển tương tác in ấn và chúng ta đã sẵn sàng cho việc in ấn.

Kết quả cuối cùng là có nhiều trình định dạng tốt hơn bất kỳ một trình định dạng tích hợp sẵn nào.

Lưu ý rằng văn bản trên đang được định dạnh bởi một UIMARKUPTEXTPRINTFORMATTER, trong khi header và hình ảnh được dựng bằng những đoạn code tuỳ chỉnh.

Printing via a Share Sheet

 Với những công cụ chúng ta đã học ở trên, thêm tính năng in trong share sheet là điều đơn giản. Thay vì sử dụng UIPrintInteractionController để hiển thị giao diện in ấn, chúng ta có thể đưa UIPrintInfo được cấu hình và những mục in, trình định dạng hoặc renderer cho UIActivityViewController. Nếu người dùng chọn vào nút Print trong share sheet, giao diện in ấn sẽ được xuất hiện với tất cả cấu hình nguyên vẹn.

Trong khi UIPrintInfo và những subclass của  UIPrintFormatter và UIPrintPageRenderer có thể được đưa tới UIActivityViewController như một sự linh hoạt, không cái nào trong số đó phù hợp với giao thức protocol  UIActivityItemSource , vì vậy chúng ta sẽ thấy một cảnh báo (vô hại) trong màn hình console là “Unknown activity items”.

Skipping the Printing UI

Cách mới trong iOS 8 là có thể in mà không cần hiển thị giao diện in ấn. Thay vì hiển thị giao diện mỗi lần người dùng nhấn vào print button, chúng ta có thể cung cấp một cách cho người dùng chọn máy in ở bất cứ nơi đâu trong ứng dụng với UIPrinterPickerController. Nó chấp chận một UIPrinter tuỳ chọn trong hàm khởi tạo của nó cho một sự lựa chọn từ trước, sử dụng những cách hiển thị tuỳ chọn tương như phần giải thích ở trên và có một completion handler khi người dùng chọn máy in:

Bây giờ bạn có thể nói cho UIPrintInteractionController để in trực tiếp bằng cách gọi printToPrinter(:completionHandler:) với máy in đã được lưu thay vì sử dụng một trong những hàm present....

Lời khuyên cuối cùng, hãy xem xét các trang in khi bạn có cách khác để tương tác với nội dung của bạn. Trong cùng một cách, bạn hãy xem xét kỹ lưỡng kích thước phông chữ hay độ tương phản giữa các yếu tố trên màn hình, phải đảm bảo để kiểm tra layout in trên giấy – độ tương phản, kích thước, và tất cả biên lề phải phù hợp với hoàn cảnh.

Bạn có thể download source code và tìm bài viết ở NSHipster blog.

Nguồn: IDE Academy via Maniacdev