Sử dụng UIImagePickerController hiệu quả hơn với Closure.
Bài đăng này đã không được cập nhật trong 6 năm
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!
All rights reserved