Sử dụng UIImagePickerController hiệu quả hơn với Closure.
Chắc hẳn làm việc với iOS, một lập trình viên chắc chắn ít nhất 1 lần làm việc với UIImagePickerController Thông thường để chọn Image từ thư viện ta làm như sau: import UIKit class OpenLibraryViewController: UIViewController { @IBOutlet weak var avatar: UIImageView! var ...
Chắc hẳn làm việc với iOS, một lập trình viên chắc chắn ít nhất 1 lần làm việc với UIImagePickerController
Thông thường để chọn Image từ thư viện ta làm như sau:
import UIKit class OpenLibraryViewController: UIViewController { @IBOutlet weak var avatar: UIImageView! var imagePicker: UIImagePickerController? @IBAction func btnChooseImage(_ sender: Any) { let alertViewController = UIAlertController(title: "Choose Image", message: "Choose your option", preferredStyle: .alert) let camera = UIAlertAction(title: "Camera", style: .default, handler: { (_) in self.openCamera() }) let gallery = UIAlertAction(title: "Gallery", style: .default) { (_) in self.openGallary() } let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (_) in //cancel } alertViewController.addAction(camera) alertViewController.addAction(gallery) alertViewController.addAction(cancel) self.present(alertViewController, animated: true, completion: nil) } } extension OpenLibraryViewController { fileprivate func openCamera() { if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) { self.imagePicker = UIImagePickerController() if let imagePicker = self.imagePicker { imagePicker.delegate = self imagePicker.sourceType = UIImagePickerControllerSourceType.camera imagePicker.allowsEditing = true self.present(imagePicker, animated: true, completion: nil) } } else { let alertWarning = UIAlertController(title: "Error", message: "Divice not have camera", preferredStyle: .alert) let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (_) in print("Cancel") } alertWarning.addAction(cancel) self.present(alertWarning, animated: true, completion: nil) } } fileprivate func openGallary() { if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary) { self.imagePicker = UIImagePickerController() if let imagePicker = self.imagePicker { imagePicker.delegate = self as UIImagePickerControllerDelegate & UINavigationControllerDelegate imagePicker.sourceType = .photoLibrary imagePicker.allowsEditing = true self.present(imagePicker, animated: true, completion: nil) } } } } extension OpenLibraryViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate { @available(iOS 2.0, *) public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { if let image = info[UIImagePickerControllerOriginalImage] as? UIImage { //handle image self.avatar.image = image } else if let image = info[UIImagePickerControllerEditedImage] as? UIImage { //handle image self.avatar.image = image } imagePicker?.dismiss(animated: true, completion: nil) } @available(iOS 2.0, *) public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { imagePicker?.dismiss(animated: true, completion: nil) } }
Khá là dài và rối, hơn nữa nó còn có 1 số nhược điểm như sau:
- Giả sử ta có 10 màn hình sử dụng muốn chọn ảnh từ thư viện -> việc lặp đi lặp lại đoạn code trên là không tốt(vi phạm nguyên tắc DRY).
- Viết 1 base chung để sử dụng, giả sử ta có 10 màn hinh, 5 màn hình muốn sử dụng tới chức năng này và 5 màn hình còn lại thì không cần sử dụng tới.
Mình sẽ làm cách khác hiệu quả hơn.
import UIKit public typealias FinishPickingMediaClosure = (UIImagePickerController, UIImage?) -> Void public typealias CancelClosure = (UIImagePickerController) -> Void private var associatedEventHandle: UInt8 = 0 extension UIImagePickerController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { private var closuresWrapper: ClosuresWrapper { get { if let wrapper = objc_getAssociatedObject(self, &associatedEventHandle) as? ClosuresWrapper { return wrapper } let closuresWrapper = ClosuresWrapper() self.closuresWrapper = closuresWrapper return closuresWrapper } set { self.delegate = self objc_setAssociatedObject(self, &associatedEventHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } // MARK: - KVO public var didFinishPickingMedia: FinishPickingMediaClosure? { set { self.closuresWrapper.didFinishPickingMedia = newValue } get { return self.closuresWrapper.didFinishPickingMedia } } public var didCancel: CancelClosure? { set { self.closuresWrapper.didCancel = newValue } get { return self.closuresWrapper.didCancel } } // MARK: - UIImagePickerControllerDelegate implementation open func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { self.closuresWrapper.didFinishPickingMedia?(picker, info["UIImagePickerControllerOriginalImage"] as? UIImage) } open func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { self.closuresWrapper.didCancel?(picker) } } fileprivate final class ClosuresWrapper { fileprivate var didFinishPickingMedia: FinishPickingMediaClosure? fileprivate var didCancel: CancelClosure? }
Ok, phân tích việc dùng Closure ở trên chút nhé
Mình muốn UIImagePickerController adopted UIImagePickerControllerDelegate, UINavigationControllerDelegate và bắt 2 sự kiện:
- Khi người sử dụng choose image trong thư viện didFinishPickingMedia
- Khi người sử dụng cancel thông qua didCancel
Chúng ta tạo ra 2 Closure tương ứng FinishPickingMediaClosure và CancelClosure
Sử dụng
class ExampleOpenUIPickerController: UIViewController { @IBOutlet weak fileprivate var avatar: UIImageView! @IBAction fileprivate func btnChooseAvatar(_ sender: Any) { let picker = UIImagePickerController() if UIImagePickerController.isSourceTypeAvailable(.camera) { picker.sourceType = .camera } picker.didCancel = { picker in picker.dismiss(animated: true, completion: nil) } picker.didFinishPickingMedia = { picker, image in self.avatar.image = image picker.dismiss(animated: true, completion: nil) } self.present(picker, animated: true, completion: nil) } }
Kết quả tương tự như trên. Với cách thiết kế như trên việc sử dụng UIImagePickerController hoàn toàn đơn giản và hiệu quả hơn rất nhiều, khắc phục nhược điểm của cách truyền thống.
Trên đây chỉ là 1 tip nhỏ để tối ưu hoá việc code sao cho ngắn, gọn và dễ sử dụng.
Hy vọng bài viết có ích cho bạn. Happy Coding!