From 02855e3f0877cc7edd8404cd8acd18759e835332 Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Fri, 2 Nov 2018 20:57:18 +0000 Subject: [PATCH 01/12] changed to swift 4.2 --- Source/FDTakeController.swift | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Source/FDTakeController.swift b/Source/FDTakeController.swift index 9dd2042..7970f33 100644 --- a/Source/FDTakeController.swift +++ b/Source/FDTakeController.swift @@ -109,6 +109,7 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa return UIApplication.shared.keyWindow!.rootViewController! }() + open var dismissOnTake = true // MARK: - Callbacks @@ -149,9 +150,8 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa open var takeVideoText: String? = nil - // MARK: - Private - private lazy var imagePicker: UIImagePickerController = { + internal lazy var imagePicker: UIImagePickerController = { [unowned self] in let retval = UIImagePickerController() retval.delegate = self @@ -159,6 +159,8 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa return retval }() + // MARK: - Private + private var alertController: UIAlertController? = nil // This is a hack required on iPad if you want to select a photo and you already have a popup on the screen @@ -239,7 +241,7 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa // http://stackoverflow.com/a/34487871/300224 let alertWindow = UIWindow(frame: UIScreen.main.bounds) alertWindow.rootViewController = UIViewController() - alertWindow.windowLevel = UIWindow.Level.alert + 1; + alertWindow.windowLevel = UIWindowLevelAlert + 1 alertWindow.makeKeyAndVisible() alertWindow.rootViewController?.present(alert, animated: true, completion: nil) return @@ -315,32 +317,35 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa extension FDTakeController : UIImagePickerControllerDelegate, UINavigationControllerDelegate { /// Conformance for ImagePicker delegate - public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { // Local variable inserted by Swift 4.2 migrator. - let info = convertFromUIImagePickerControllerInfoKeyDictionary(info) UIApplication.shared.isStatusBarHidden = true - let mediaType: String = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaType)] as! String + let mediaType: String = info[UIImagePickerControllerMediaType] as! String var imageToSave: UIImage // Handle a still image capture if mediaType == kUTTypeImage as String { - if let editedImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.editedImage)] as? UIImage { + if let editedImage = info[UIImagePickerControllerEditedImage] as? UIImage { imageToSave = editedImage - } else if let originalImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage { + } else if let originalImage = info[UIImagePickerControllerOriginalImage] as? UIImage { imageToSave = originalImage } else { self.didCancel?() return } + + self.didGetPhoto?(imageToSave, info) if UI_USER_INTERFACE_IDIOM() == .pad { self.imagePicker.dismiss(animated: true) } } else if mediaType == kUTTypeMovie as String { - self.didGetVideo?(info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaURL)] as! URL, info) + self.didGetVideo?(info[UIImagePickerControllerMediaURL] as! URL, info) } - picker.dismiss(animated: true, completion: nil) + if self.dismissOnTake { + picker.dismiss(animated: true, completion: nil) + } } /// Conformance for image picker delegate @@ -350,14 +355,11 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro self.didDeny?() } - // Helper function inserted by Swift 4.2 migrator. + /* // Helper function inserted by Swift 4.2 migrator. private func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] { return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)}) - } + } */ - // Helper function inserted by Swift 4.2 migrator. - private func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String { - return input.rawValue - } + } From 8332e0425939eddfe25d85ab9a98d5a8ba2f90c6 Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Fri, 2 Nov 2018 21:00:46 +0000 Subject: [PATCH 02/12] info.plist --- FDTake.xcodeproj/project.pbxproj | 4 ++-- Source/Info.plist | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/FDTake.xcodeproj/project.pbxproj b/FDTake.xcodeproj/project.pbxproj index d662089..b031198 100644 --- a/FDTake.xcodeproj/project.pbxproj +++ b/FDTake.xcodeproj/project.pbxproj @@ -424,7 +424,7 @@ INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "net.phor.--PROJECT-NAME--"; + PRODUCT_BUNDLE_IDENTIFIER = net.phor.FDTake; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -444,7 +444,7 @@ INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "net.phor.--PROJECT-NAME--"; + PRODUCT_BUNDLE_IDENTIFIER = net.phor.FDTake; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 4.2; diff --git a/Source/Info.plist b/Source/Info.plist index fbe1e6b..ccc22a6 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion en + CFBundleDisplayName + FDTake CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -17,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 1 NSPrincipalClass From 568cb47c20fd580052bbed56d9054a8792e895d0 Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sat, 3 Nov 2018 13:23:10 +0000 Subject: [PATCH 03/12] pickerController ref --- Source/FDTakeController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/FDTakeController.swift b/Source/FDTakeController.swift index 7970f33..4ea6633 100644 --- a/Source/FDTakeController.swift +++ b/Source/FDTakeController.swift @@ -53,7 +53,7 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa // MARK: - Initializers & Class Convenience Methods /// Convenience method for getting a photo - open class func getPhotoWithCallback(getPhotoWithCallback callback: @escaping (_ photo: UIImage, _ info: [AnyHashable: Any]) -> Void) { + open class func getPhotoWithCallback(getPhotoWithCallback callback: @escaping (_ photo: UIImage, _ info: [AnyHashable: Any], _ picker: UIImagePickerController?) -> Void) { let fdTake = FDTakeController() fdTake.allowsVideo = false fdTake.didGetPhoto = callback @@ -114,7 +114,7 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa // MARK: - Callbacks /// A photo was selected - open var didGetPhoto: ((_ photo: UIImage, _ info: [AnyHashable: Any]) -> Void)? + open var didGetPhoto: ((_ photo: UIImage, _ info: [AnyHashable: Any], _ picker: UIImagePickerController?) -> Void)? /// A video was selected open var didGetVideo: ((_ video: URL, _ info: [AnyHashable: Any]) -> Void)? @@ -335,7 +335,7 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro } - self.didGetPhoto?(imageToSave, info) + self.didGetPhoto?(imageToSave, info, picker) if UI_USER_INTERFACE_IDIOM() == .pad { self.imagePicker.dismiss(animated: true) } From bbcc26c57af3fc3405ba482be01e950dafe6502f Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sat, 3 Nov 2018 19:21:23 +0000 Subject: [PATCH 04/12] working crop vc --- FDTake.podspec | 4 +- Source/FDTakeController.swift | 124 +++++++----- Source/ImageCropper.swift | 293 ++++++++++++++++++++++++++++ Source/UIImage+FixOrientation.swift | 56 ++++++ 4 files changed, 430 insertions(+), 47 deletions(-) create mode 100644 Source/ImageCropper.swift create mode 100644 Source/UIImage+FixOrientation.swift diff --git a/FDTake.podspec b/FDTake.podspec index 625ba97..613a3b2 100644 --- a/FDTake.podspec +++ b/FDTake.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "FDTake" - s.version = "2.0.2" - s.summary = "Easily take a photo or video or choose from library" + s.version = "2.0.3" + s.summary = "Easily take a photo or video or choose from library. Also user can personalize edit crop" s.description = <<-DESC `FDTake` helps you quickly have the user take or choose an existing photo or video. DESC diff --git a/Source/FDTakeController.swift b/Source/FDTakeController.swift index 4ea6633..e9dbe20 100644 --- a/Source/FDTakeController.swift +++ b/Source/FDTakeController.swift @@ -53,7 +53,7 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa // MARK: - Initializers & Class Convenience Methods /// Convenience method for getting a photo - open class func getPhotoWithCallback(getPhotoWithCallback callback: @escaping (_ photo: UIImage, _ info: [AnyHashable: Any], _ picker: UIImagePickerController?) -> Void) { + open class func getPhotoWithCallback(getPhotoWithCallback callback: @escaping (_ photo: UIImage, _ info: [AnyHashable: Any]?) -> Void) { let fdTake = FDTakeController() fdTake.allowsVideo = false fdTake.didGetPhoto = callback @@ -109,12 +109,12 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa return UIApplication.shared.keyWindow!.rootViewController! }() - open var dismissOnTake = true + open var aspectRatio: CGFloat = 4/3 // MARK: - Callbacks /// A photo was selected - open var didGetPhoto: ((_ photo: UIImage, _ info: [AnyHashable: Any], _ picker: UIImagePickerController?) -> Void)? + open var didGetPhoto: ((_ photo: UIImage, _ info: [AnyHashable: Any]?) -> Void)? /// A video was selected open var didGetVideo: ((_ video: URL, _ info: [AnyHashable: Any]) -> Void)? @@ -150,14 +150,14 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa open var takeVideoText: String? = nil - + internal var info: [AnyHashable: Any]? + internal lazy var imagePicker: UIImagePickerController = { - [unowned self] in - let retval = UIImagePickerController() - retval.delegate = self - retval.allowsEditing = true - return retval - }() + let picker = UIImagePickerController() + picker.delegate = self + picker.allowsEditing = false + return picker + }() // MARK: - Private @@ -208,6 +208,37 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa } } + fileprivate func startImagePicker(_ title: FDTakeControllerLocalizableStrings, _ source: UIImagePickerController.SourceType) { + + + self.imagePicker.sourceType = source + if source == .camera && self.defaultsToFrontCamera && UIImagePickerController.isCameraDeviceAvailable(.front) { + self.imagePicker.cameraDevice = .front + } + // set the media type: photo or video + self.imagePicker.allowsEditing = false + var mediaTypes = [String]() + if self.allowsPhoto { + mediaTypes.append(String(kUTTypeImage)) + } + if self.allowsVideo { + mediaTypes.append(String(kUTTypeMovie)) + } + self.imagePicker.mediaTypes = mediaTypes + + //TODO: Need to encapsulate popover code + var popOverPresentRect: CGRect = self.presentingRect ?? CGRect(x: 0, y: 0, width: 1, height: 1) + if popOverPresentRect.size.height == 0 || popOverPresentRect.size.width == 0 { + popOverPresentRect = CGRect(x: 0, y: 0, width: 1, height: 1) + } + + if !(UI_USER_INTERFACE_IDIOM() == .phone || (source == .camera && self.iPadUsesFullScreenCamera)) { + // On iPad use pop-overs. + self.imagePicker.modalPresentationStyle = .popover + self.imagePicker.popoverPresentationController?.sourceRect = popOverPresentRect + } + + } /// Presents the user with an option to take a photo or choose a photo from the library open func present() { @@ -256,36 +287,9 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa for (title, source) in titleToSource { let action = UIAlertAction(title: localizeString(title), style: .default) { (UIAlertAction) -> Void in - self.imagePicker.sourceType = source - if source == .camera && self.defaultsToFrontCamera && UIImagePickerController.isCameraDeviceAvailable(.front) { - self.imagePicker.cameraDevice = .front - } - // set the media type: photo or video - self.imagePicker.allowsEditing = self.allowsEditing - var mediaTypes = [String]() - if self.allowsPhoto { - mediaTypes.append(String(kUTTypeImage)) - } - if self.allowsVideo { - mediaTypes.append(String(kUTTypeMovie)) - } - self.imagePicker.mediaTypes = mediaTypes - - //TODO: Need to encapsulate popover code - var popOverPresentRect: CGRect = self.presentingRect ?? CGRect(x: 0, y: 0, width: 1, height: 1) - if popOverPresentRect.size.height == 0 || popOverPresentRect.size.width == 0 { - popOverPresentRect = CGRect(x: 0, y: 0, width: 1, height: 1) - } + self.startImagePicker(title,source) let topVC = self.topViewController(rootViewController: self.presentingViewController) - - if UI_USER_INTERFACE_IDIOM() == .phone || (source == .camera && self.iPadUsesFullScreenCamera) { - topVC.present(self.imagePicker, animated: true, completion: nil) - } else { - // On iPad use pop-overs. - self.imagePicker.modalPresentationStyle = .popover - self.imagePicker.popoverPresentationController?.sourceRect = popOverPresentRect - topVC.present(self.imagePicker, animated: true, completion: nil) - } + topVC.present(self.imagePicker, animated: true, completion: nil) } alertController!.addAction(action) } @@ -335,17 +339,35 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro } - self.didGetPhoto?(imageToSave, info, picker) - if UI_USER_INTERFACE_IDIOM() == .pad { - self.imagePicker.dismiss(animated: true) + + if self.allowsEditing { + let cropper = UIImageCropper(cropRatio: self.aspectRatio) + cropper.cropButtonText = "Crop" // button labes can be localised/changed + cropper.cancelButtonText = "Cancel" + + cropper.image = imageToSave.fixOrientation() + cropper.delegate = self + + + picker.present(cropper, animated: true, completion: nil) + + + self.info = info + } else { + self.didGetPhoto?(imageToSave, info) + + if UI_USER_INTERFACE_IDIOM() == .pad { + self.imagePicker.dismiss(animated: true) + } + + picker.dismiss(animated: true, completion: nil) } + + } else if mediaType == kUTTypeMovie as String { self.didGetVideo?(info[UIImagePickerControllerMediaURL] as! URL, info) } - if self.dismissOnTake { - picker.dismiss(animated: true, completion: nil) - } } /// Conformance for image picker delegate @@ -360,6 +382,18 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)}) } */ +} + +extension FDTakeController: UIImageCropperProtocol { + public func didCropImage(originalImage: UIImage?, croppedImage: UIImage?) { + + if let image = croppedImage { + self.didGetPhoto?(image, self.info) + } else { + self.didFail?() + } + self.dismiss() + } } diff --git a/Source/ImageCropper.swift b/Source/ImageCropper.swift new file mode 100644 index 0000000..af46c76 --- /dev/null +++ b/Source/ImageCropper.swift @@ -0,0 +1,293 @@ +// +// UIImageCropper.swift +// UIImageCropper +// +// Created by Jari Kalinainen jari@klubitii.com +// +// Licensed under MIT License 2017 +// + +import UIKit + +@objc public protocol UIImageCropperProtocol: class { + /// Called when user presses crop button (or when there is unknown situation (one or both images will be nil)). + /// - parameter originalImage + /// Orginal image from camera/gallery + /// - parameter croppedImage + /// Cropped image in cropRatio aspect ratio + func didCropImage(originalImage: UIImage?, croppedImage: UIImage?) +} + +public class UIImageCropper: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { + var presenting = false + /// Aspect ratio of the cropped image + public var cropRatio: CGFloat = 1 + /// delegate that implements UIImageCropperProtocol + public weak var delegate: UIImageCropperProtocol? + + /// Crop button text + public var cropButtonText: String = "Crop" + /// Retake/Cancel button text + public var cancelButtonText: String = "Retake" + + /// original image from camera or gallery + public var image: UIImage? { + didSet { + guard let image = self.image else { + return + } + layoutDone = false + ratio = image.size.height / image.size.width + imageView.image = image + self.view.layoutIfNeeded() + } + } + /// cropped image + public var cropImage: UIImage? { + return crop() + } + + + private let topView = UIView() + private let fadeView = UIView() + private let imageView: UIImageView = UIImageView() + private let cropView: UIView = UIView() + + private var topConst: NSLayoutConstraint? + private var leadConst: NSLayoutConstraint? + + private var imageHeightConst: NSLayoutConstraint? + private var imageWidthConst: NSLayoutConstraint? + + private var ratio: CGFloat = 1 + private var layoutDone: Bool = false + + private var orgHeight: CGFloat = 0 + private var orgWidth: CGFloat = 0 + private var topStart: CGFloat = 0 + private var leadStart: CGFloat = 0 + private var pinchStart: CGPoint = .zero + + private let cropButton = UIButton(type: .custom) + private let cancelButton = UIButton(type: .custom) + + //MARK: - inits + /// initializer + /// - parameter cropRatio + /// Aspect ratio of the cropped image + convenience public init(cropRatio: CGFloat) { + self.init() + self.cropRatio = cropRatio + } + + //MARK: - overrides + override public func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = UIColor.black + + //main views + topView.backgroundColor = UIColor.clear + let bottomView = UIView() + bottomView.backgroundColor = UIColor.black.withAlphaComponent(0.7) + self.view.addSubview(topView) + self.view.addSubview(bottomView) + topView.translatesAutoresizingMaskIntoConstraints = false + bottomView.translatesAutoresizingMaskIntoConstraints = false + let horizontalTopConst = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[view]-(0)-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["view": topView]) + let horizontalBottomConst = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[view]-(0)-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["view": bottomView]) + let verticalConst = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[top]-(0)-[bottom(70)]-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["bottom": bottomView, "top": topView]) + self.view.addConstraints(horizontalTopConst + horizontalBottomConst + verticalConst) + + // image view + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + topView.addSubview(imageView) + topConst = NSLayoutConstraint(item: imageView, attribute: .top, relatedBy: .equal, toItem: topView, attribute: .top, multiplier: 1, constant: 0) + topConst?.priority = .defaultHigh + leadConst = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: topView, attribute: .leading, multiplier: 1, constant: 0) + leadConst?.priority = .defaultHigh + imageWidthConst = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 1) + imageWidthConst?.priority = .required + imageHeightConst = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 1) + imageHeightConst?.priority = .required + imageView.addConstraints([imageHeightConst!, imageWidthConst!]) + topView.addConstraints([topConst!, leadConst!]) + imageView.image = self.image + + // imageView gestures + let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinch)) + imageView.addGestureRecognizer(pinchGesture) + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(pan)) + imageView.addGestureRecognizer(panGesture) + imageView.isUserInteractionEnabled = true + + //fade overlay + fadeView.translatesAutoresizingMaskIntoConstraints = false + fadeView.isUserInteractionEnabled = false + fadeView.backgroundColor = UIColor.black.withAlphaComponent(0.3) + topView.addSubview(fadeView) + let horizontalFadeConst = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(0)-[view]-(0)-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["view": fadeView]) + let verticalFadeConst = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[view]-(0)-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["view": fadeView]) + topView.addConstraints(horizontalFadeConst + verticalFadeConst) + + // crop overlay + cropView.translatesAutoresizingMaskIntoConstraints = false + cropView.isUserInteractionEnabled = false + topView.addSubview(cropView) + let centerXConst = NSLayoutConstraint(item: cropView, attribute: .centerX, relatedBy: .equal, toItem: topView, attribute: .centerX, multiplier: 1, constant: 0) + let centerYConst = NSLayoutConstraint(item: cropView, attribute: .centerY, relatedBy: .equal, toItem: topView, attribute: .centerY, multiplier: 1, constant: 0) + let widthConst = NSLayoutConstraint(item: cropView, attribute: .width, relatedBy: .equal, toItem: topView, attribute: .width, multiplier: 0.9, constant: 0) + widthConst.priority = .defaultHigh + let heightConst = NSLayoutConstraint(item: cropView, attribute: .height, relatedBy: .lessThanOrEqual, toItem: topView, attribute: .height, multiplier: 0.9, constant: 0) + let ratioConst = NSLayoutConstraint(item: cropView, attribute: .width, relatedBy: .equal, toItem: cropView, attribute: .height, multiplier: cropRatio, constant: 0) + cropView.addConstraints([ratioConst]) + topView.addConstraints([widthConst, heightConst, centerXConst, centerYConst]) + cropView.layer.borderWidth = 1 + cropView.layer.borderColor = UIColor.white.cgColor + cropView.backgroundColor = UIColor.clear + + // control buttons + let cropCenterXMultiplier: CGFloat = 1.0 + /* + cancelButton.translatesAutoresizingMaskIntoConstraints = false + cancelButton.setTitle(cancelButtonText, for: .normal) + cancelButton.addTarget(self, action: #selector(cropCancel), for: .touchUpInside) + bottomView.addSubview(cancelButton) + let centerCancelXConst = NSLayoutConstraint(item: cancelButton, attribute: .centerX, relatedBy: .equal, toItem: bottomView, attribute: .centerX, multiplier: 0.5, constant: 0) + let centerCancelYConst = NSLayoutConstraint(item: cancelButton, attribute: .centerY, relatedBy: .equal, toItem: bottomView, attribute: .centerY, multiplier: 1, constant: 0) + bottomView.addConstraints([centerCancelXConst, centerCancelYConst]) + cropCenterXMultiplier = 1.5 */ + + cropButton.translatesAutoresizingMaskIntoConstraints = false + cropButton.addTarget(self, action: #selector(cropDone), for: .touchUpInside) + bottomView.addSubview(cropButton) + let centerCropXConst = NSLayoutConstraint(item: cropButton, attribute: .centerX, relatedBy: .equal, toItem: bottomView, attribute: .centerX, multiplier: cropCenterXMultiplier, constant: 0) + let centerCropYConst = NSLayoutConstraint(item: cropButton, attribute: .centerY, relatedBy: .equal, toItem: bottomView, attribute: .centerY, multiplier: 1, constant: 0) + bottomView.addConstraints([centerCropXConst, centerCropYConst]) + + self.view.bringSubview(toFront: bottomView) + + bottomView.layoutIfNeeded() + topView.layoutIfNeeded() + } + + override public func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.cancelButton.setTitle(cancelButtonText, for: .normal) + self.cropButton.setTitle(cropButtonText, for: .normal) + + if image == nil { + self.dismiss(animated: true, completion: nil) + } + } + + override public func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + guard !layoutDone else { + return + } + layoutDone = true + + if ratio < 1 { + imageWidthConst?.constant = cropView.frame.height / ratio + imageHeightConst?.constant = cropView.frame.height + } else { + imageWidthConst?.constant = cropView.frame.width + imageHeightConst?.constant = cropView.frame.width * ratio + } + + let horizontal = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(<=\(cropView.frame.origin.x))-[view]-(<=\(cropView.frame.origin.x))-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["view": imageView]) + let vertical = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(<=\(cropView.frame.origin.y))-[view]-(<=\(cropView.frame.origin.y))-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["view": imageView]) + topView.addConstraints(horizontal + vertical) + + maskFadeView() + orgWidth = imageWidthConst!.constant + orgHeight = imageHeightConst!.constant + } + + private func maskFadeView() { + let path = UIBezierPath(rect: cropView.frame) + path.append(UIBezierPath(rect: fadeView.frame)) + let mask = CAShapeLayer() + mask.fillRule = kCAFillRuleEvenOdd + mask.path = path.cgPath + fadeView.layer.mask = mask + } + + //MARK: - button actions + @objc func cropDone() { + presenting = false + self.dismiss(animated: false, completion: { + + self.delegate?.didCropImage(originalImage: self.image, croppedImage: self.cropImage) + }) + + } + + @objc func cropCancel() { + presenting = false + self.dismiss(animated: true, completion: nil) + //self.delegate?.didCancelCrop() + + } + + //MARK: - gesture handling + @objc func pinch(_ pinch: UIPinchGestureRecognizer) { + if pinch.state == .began { + orgWidth = imageWidthConst!.constant + orgHeight = imageHeightConst!.constant + pinchStart = pinch.location(in: self.view) + } + let scale = pinch.scale + let height = max(orgHeight * scale, cropView.frame.height) + let width = max(orgWidth * scale, cropView.frame.height / ratio) + imageHeightConst?.constant = height + imageWidthConst?.constant = width + } + + @objc func pan(_ pan: UIPanGestureRecognizer) { + if pan.state == .began { + topStart = topConst!.constant + leadStart = leadConst!.constant + } + let trans = pan.translation(in: self.view) + leadConst?.constant = leadStart + trans.x + topConst?.constant = topStart + trans.y + } + + //MARK: - cropping done here + private func crop() -> UIImage? { + guard let image = self.image else { + return nil + } + let imageSize = image.size + let width = cropView.frame.width / imageView.frame.width + let height = cropView.frame.height / imageView.frame.height + let x = (cropView.frame.origin.x - imageView.frame.origin.x) / imageView.frame.width + let y = (cropView.frame.origin.y - imageView.frame.origin.y) / imageView.frame.height + + let cropFrame = CGRect(x: x * imageSize.width, y: y * imageSize.height, width: imageSize.width * width, height: imageSize.height * height) + if let cropCGImage = image.cgImage?.cropping(to: cropFrame) { + let cropImage = UIImage(cgImage: cropCGImage, scale: 1, orientation: .up) + return cropImage + } + return nil + } + +} + +extension UIView { + func constraintToFill(superView view: UIView?) { + guard let view = view else { + assertionFailure("superview is nil") + return + } + self.translatesAutoresizingMaskIntoConstraints = false + self.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true + self.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true + self.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + self.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + } +} diff --git a/Source/UIImage+FixOrientation.swift b/Source/UIImage+FixOrientation.swift new file mode 100644 index 0000000..4e8d142 --- /dev/null +++ b/Source/UIImage+FixOrientation.swift @@ -0,0 +1,56 @@ +// +// + +import UIKit + +extension UIImage { + + func fixOrientation() -> UIImage { + + if imageOrientation == .up { + return self + } + + var transform: CGAffineTransform = CGAffineTransform.identity + + switch imageOrientation { + case .down, .downMirrored: + transform = transform.translatedBy(x: size.width, y: size.height) + transform = transform.rotated(by: .pi) + case .left, .leftMirrored: + transform = transform.translatedBy(x: size.width, y: 0) + transform = transform.rotated(by: .pi/2) + case .right, .rightMirrored: + transform = transform.translatedBy(x: 0, y: size.height) + transform = transform.rotated(by: -.pi/2) + default: //.up, .upMirrored + break + } + + switch imageOrientation { + case .upMirrored, .downMirrored: + transform.translatedBy(x: size.width, y: 0) + transform.scaledBy(x: -1, y: 1) + case .leftMirrored, .rightMirrored: + transform.translatedBy(x: size.height, y: 0) + transform.scaledBy(x: -1, y: 1) + default: //.up, .down, .left, .right + break + } + + let ctx: CGContext = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: self.cgImage!.bitsPerComponent, bytesPerRow: 0, space: (self.cgImage?.colorSpace)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)! + + ctx.concatenate(transform) + + switch imageOrientation { + case .left, .leftMirrored, .right, .rightMirrored: + ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width)) + default: + ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) + } + + let cgImage: CGImage = ctx.makeImage()! + + return UIImage(cgImage: cgImage) + } +} From 511e498a37cd0a9b7800827e7b4bcabececbdac0 Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sat, 3 Nov 2018 19:32:51 +0000 Subject: [PATCH 05/12] updated README --- FDTake.podspec | 2 +- README.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/FDTake.podspec b/FDTake.podspec index 613a3b2..da2e1eb 100644 --- a/FDTake.podspec +++ b/FDTake.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "FDTake" s.version = "2.0.3" - s.summary = "Easily take a photo or video or choose from library. Also user can personalize edit crop" + s.summary = "Easily take a photo or video or choose from library. Also user can personalize edit cropping" s.description = <<-DESC `FDTake` helps you quickly have the user take or choose an existing photo or video. DESC diff --git a/README.md b/README.md index 3ed053f..5616458 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ Easily take a photo or video or choose from library + +**Also**, you can edit the taken photos and crop them within your needs (defining custom crop aspect ratio). This functionality was taken from **jvk75's** [UIImageCropper](https://github.com/jvk75/UIImageCropper) + **:beer: Author's tip jar: https://amazon.com/hz/wishlist/ls/EE78A23EEGQB** ## Usage @@ -51,6 +54,9 @@ open var allowsSelectFromLibrary: Bool /// Whether to allow editing the media after capturing/selection open var allowsEditing: Bool +/// Aspect ratio when cropping a photo +open var aspectRatio: CGFloat = 4/3 + /// Whether to use full screen camera preview on the iPad open var iPadUsesFullScreenCamera: Bool From 09f59f631b841aefa79e301e37e22e616cb9e4eb Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sat, 3 Nov 2018 19:38:31 +0000 Subject: [PATCH 06/12] updated CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98420fd..e3e4221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ All notable changes to this project will be documented in this file. - Localization is broken, discuss at https://github.com/fulldecent/FDTake/pull/99 +## [2.0.3](https://github.com/fulldecent/FDBarGuage/compare/2.0.3) + +#### Updated + +- Add ImageCrop ViewController (with custom aspect ratio) + ## [2.0.2](https://github.com/fulldecent/FDBarGuage/compare/2.0.2) #### Updated From e729aca28ab0c37146341633f38a25e7c155bf8b Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sat, 3 Nov 2018 19:39:58 +0000 Subject: [PATCH 07/12] updated CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3e4221..73f4e2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ All notable changes to this project will be documented in this file. #### Updated -- Add ImageCrop ViewController (with custom aspect ratio) +- Add ImageCrop ViewController (with custom aspect ratio) ## [2.0.2](https://github.com/fulldecent/FDBarGuage/compare/2.0.2) From c69d8c7c4208ac8d007d18df3a8d01006b196042 Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sat, 3 Nov 2018 19:47:26 +0000 Subject: [PATCH 08/12] re-updated to swift4 --- FDTake.xcodeproj/project.pbxproj | 10 ++++++++++ Source/FDTakeController.swift | 25 +++++++++++++++---------- Source/ImageCropper.swift | 4 ++-- iOS Example/Source/ViewController.swift | 2 +- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/FDTake.xcodeproj/project.pbxproj b/FDTake.xcodeproj/project.pbxproj index b031198..429ce39 100644 --- a/FDTake.xcodeproj/project.pbxproj +++ b/FDTake.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + D07FC161218E31EF00310E7D /* ImageCropper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07FC15F218E31EE00310E7D /* ImageCropper.swift */; }; + D07FC162218E31EF00310E7D /* UIImage+FixOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07FC160218E31EF00310E7D /* UIImage+FixOrientation.swift */; }; D95E1BE71D88D63400F94976 /* FDTake.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D95E1BDD1D88D63300F94976 /* FDTake.framework */; }; D95E1BEC1D88D63400F94976 /* FDTakeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D95E1BEB1D88D63400F94976 /* FDTakeTests.swift */; }; D95E1BEE1D88D63400F94976 /* FDTake.h in Headers */ = {isa = PBXBuildFile; fileRef = D95E1BE01D88D63300F94976 /* FDTake.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -25,6 +27,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + D07FC15F218E31EE00310E7D /* ImageCropper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCropper.swift; sourceTree = ""; }; + D07FC160218E31EF00310E7D /* UIImage+FixOrientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+FixOrientation.swift"; sourceTree = ""; }; D95E1BDD1D88D63300F94976 /* FDTake.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FDTake.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D95E1BE01D88D63300F94976 /* FDTake.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FDTake.h; sourceTree = ""; }; D95E1BE11D88D63300F94976 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -93,6 +97,8 @@ D95E1BDF1D88D63300F94976 /* Source */ = { isa = PBXGroup; children = ( + D07FC15F218E31EE00310E7D /* ImageCropper.swift */, + D07FC160218E31EF00310E7D /* UIImage+FixOrientation.swift */, D9D7F7B81D8DAC4F00A0DA5B /* FDTakeController.swift */, D95E1BE01D88D63300F94976 /* FDTake.h */, D95E1BE11D88D63300F94976 /* Info.plist */, @@ -241,7 +247,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D07FC162218E31EF00310E7D /* UIImage+FixOrientation.swift in Sources */, D9D7F7B91D8DAC4F00A0DA5B /* FDTakeController.swift in Sources */, + D07FC161218E31EF00310E7D /* ImageCropper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -350,6 +358,7 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -405,6 +414,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Source/FDTakeController.swift b/Source/FDTakeController.swift index e9dbe20..f5d5e53 100644 --- a/Source/FDTakeController.swift +++ b/Source/FDTakeController.swift @@ -272,7 +272,7 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa // http://stackoverflow.com/a/34487871/300224 let alertWindow = UIWindow(frame: UIScreen.main.bounds) alertWindow.rootViewController = UIViewController() - alertWindow.windowLevel = UIWindowLevelAlert + 1 + alertWindow.windowLevel = UIWindow.Level.alert + 1 alertWindow.makeKeyAndVisible() alertWindow.rootViewController?.present(alert, animated: true, completion: nil) return @@ -321,17 +321,18 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa extension FDTakeController : UIImagePickerControllerDelegate, UINavigationControllerDelegate { /// Conformance for ImagePicker delegate - public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { + public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:[UIImagePickerController.InfoKey : Any]) { // Local variable inserted by Swift 4.2 migrator. - + let info = convertFromUIImagePickerControllerInfoKeyDictionary(info) + UIApplication.shared.isStatusBarHidden = true - let mediaType: String = info[UIImagePickerControllerMediaType] as! String + let mediaType: String = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaType)] as! String var imageToSave: UIImage // Handle a still image capture if mediaType == kUTTypeImage as String { - if let editedImage = info[UIImagePickerControllerEditedImage] as? UIImage { + if let editedImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.editedImage)] as? UIImage { imageToSave = editedImage - } else if let originalImage = info[UIImagePickerControllerOriginalImage] as? UIImage { + } else if let originalImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage { imageToSave = originalImage } else { self.didCancel?() @@ -365,7 +366,7 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro } else if mediaType == kUTTypeMovie as String { - self.didGetVideo?(info[UIImagePickerControllerMediaURL] as! URL, info) + self.didGetVideo?(info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaURL)] as! URL, info) } } @@ -376,11 +377,15 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro picker.dismiss(animated: true, completion: nil) self.didDeny?() } - - /* // Helper function inserted by Swift 4.2 migrator. + // Helper function inserted by Swift 4.2 migrator. private func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] { return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)}) - } */ + } + + // Helper function inserted by Swift 4.2 migrator. + private func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String { + return input.rawValue + } } diff --git a/Source/ImageCropper.swift b/Source/ImageCropper.swift index af46c76..5873db1 100644 --- a/Source/ImageCropper.swift +++ b/Source/ImageCropper.swift @@ -166,7 +166,7 @@ public class UIImageCropper: UIViewController, UIImagePickerControllerDelegate, let centerCropYConst = NSLayoutConstraint(item: cropButton, attribute: .centerY, relatedBy: .equal, toItem: bottomView, attribute: .centerY, multiplier: 1, constant: 0) bottomView.addConstraints([centerCropXConst, centerCropYConst]) - self.view.bringSubview(toFront: bottomView) + self.view.bringSubviewToFront(bottomView) bottomView.layoutIfNeeded() topView.layoutIfNeeded() @@ -211,7 +211,7 @@ public class UIImageCropper: UIViewController, UIImagePickerControllerDelegate, let path = UIBezierPath(rect: cropView.frame) path.append(UIBezierPath(rect: fadeView.frame)) let mask = CAShapeLayer() - mask.fillRule = kCAFillRuleEvenOdd + mask.fillRule = CAShapeLayerFillRule.evenOdd mask.path = path.cgPath fadeView.layer.mask = mask } diff --git a/iOS Example/Source/ViewController.swift b/iOS Example/Source/ViewController.swift index d30032f..c2a41fe 100644 --- a/iOS Example/Source/ViewController.swift +++ b/iOS Example/Source/ViewController.swift @@ -52,7 +52,7 @@ class ViewController: UIViewController { self.present(alert, animated: true, completion: nil) } fdTakeController.didGetPhoto = { - (photo: UIImage, info: [AnyHashable : Any]) -> Void in + (photo: UIImage, info: [AnyHashable : Any]?) -> Void in let alert = UIAlertController(title: "Got photo", message: "User selected photo", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) From 9dc9fc4d14206fcac6a3d2149d0c7e2748f28ce5 Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sat, 3 Nov 2018 19:55:09 +0000 Subject: [PATCH 09/12] re-updated to swift4 --- iOS Example/iOS Example.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iOS Example/iOS Example.xcodeproj/project.pbxproj b/iOS Example/iOS Example.xcodeproj/project.pbxproj index 518f12a..6e2bd64 100644 --- a/iOS Example/iOS Example.xcodeproj/project.pbxproj +++ b/iOS Example/iOS Example.xcodeproj/project.pbxproj @@ -244,6 +244,7 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -295,6 +296,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; From 61945e516ec5af0bcc16d67a4caab67a6c105840 Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sun, 10 Mar 2019 21:16:53 +0000 Subject: [PATCH 10/12] working pod --- FDTake.xcodeproj/project.pbxproj | 16 ++++++++++++---- {Source => Supporting Files}/Info.plist | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) rename {Source => Supporting Files}/Info.plist (99%) diff --git a/FDTake.xcodeproj/project.pbxproj b/FDTake.xcodeproj/project.pbxproj index 429ce39..e970fe4 100644 --- a/FDTake.xcodeproj/project.pbxproj +++ b/FDTake.xcodeproj/project.pbxproj @@ -29,9 +29,9 @@ /* Begin PBXFileReference section */ D07FC15F218E31EE00310E7D /* ImageCropper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCropper.swift; sourceTree = ""; }; D07FC160218E31EF00310E7D /* UIImage+FixOrientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+FixOrientation.swift"; sourceTree = ""; }; + D0BA674D2235B5BF003B30D9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D95E1BDD1D88D63300F94976 /* FDTake.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FDTake.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D95E1BE01D88D63300F94976 /* FDTake.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FDTake.h; sourceTree = ""; }; - D95E1BE11D88D63300F94976 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D95E1BE61D88D63400F94976 /* FDTakeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FDTakeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D95E1BEB1D88D63400F94976 /* FDTakeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FDTakeTests.swift; sourceTree = ""; }; D95E1BED1D88D63400F94976 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -76,9 +76,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + D0BA674C2235B5BF003B30D9 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D0BA674D2235B5BF003B30D9 /* Info.plist */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; D95E1BD31D88D63200F94976 = { isa = PBXGroup; children = ( + D0BA674C2235B5BF003B30D9 /* Supporting Files */, D95E1BDF1D88D63300F94976 /* Source */, D95E1BEA1D88D63400F94976 /* Tests */, D95E1BDE1D88D63300F94976 /* Products */, @@ -101,7 +110,6 @@ D07FC160218E31EF00310E7D /* UIImage+FixOrientation.swift */, D9D7F7B81D8DAC4F00A0DA5B /* FDTakeController.swift */, D95E1BE01D88D63300F94976 /* FDTake.h */, - D95E1BE11D88D63300F94976 /* Info.plist */, D983D7531D8DDA0700E4223A /* Localizable.strings */, ); path = Source; @@ -431,7 +439,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Info.plist; + INFOPLIST_FILE = "Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.phor.FDTake; @@ -451,7 +459,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Source/Info.plist; + INFOPLIST_FILE = "Supporting Files/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.phor.FDTake; diff --git a/Source/Info.plist b/Supporting Files/Info.plist similarity index 99% rename from Source/Info.plist rename to Supporting Files/Info.plist index ccc22a6..69c12a5 100644 --- a/Source/Info.plist +++ b/Supporting Files/Info.plist @@ -24,3 +24,4 @@ + From 8fb0d6aeee32a05e94cae77bfea3e24169421bdc Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Sun, 10 Mar 2019 21:23:54 +0000 Subject: [PATCH 11/12] 4.2 version --- FDTake.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FDTake.podspec b/FDTake.podspec index da2e1eb..ccd1c07 100644 --- a/FDTake.podspec +++ b/FDTake.podspec @@ -13,6 +13,6 @@ Pod::Spec.new do |s| s.social_media_url = 'https://twitter.com/fulldecent' s.platform = :ios, '10.0' s.requires_arc = true - + s.swift_version = '4.2' s.source_files = 'Source/**/*' end From 792b1e33ee99558ac665af7cd783f5e1bc57b0df Mon Sep 17 00:00:00 2001 From: Carlos Correia Date: Tue, 2 Jul 2019 13:43:56 +0100 Subject: [PATCH 12/12] sourceView --- Source/FDTakeController.swift | 3 ++- iOS Example/Source/ViewController.swift | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/FDTakeController.swift b/Source/FDTakeController.swift index f5d5e53..52e721b 100644 --- a/Source/FDTakeController.swift +++ b/Source/FDTakeController.swift @@ -235,7 +235,8 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa if !(UI_USER_INTERFACE_IDIOM() == .phone || (source == .camera && self.iPadUsesFullScreenCamera)) { // On iPad use pop-overs. self.imagePicker.modalPresentationStyle = .popover - self.imagePicker.popoverPresentationController?.sourceRect = popOverPresentRect + self.imagePicker.popoverPresentationController!.sourceView = self.presentingView! + self.imagePicker.popoverPresentationController!.permittedArrowDirections = .any } } diff --git a/iOS Example/Source/ViewController.swift b/iOS Example/Source/ViewController.swift index c2a41fe..79ed77f 100644 --- a/iOS Example/Source/ViewController.swift +++ b/iOS Example/Source/ViewController.swift @@ -81,12 +81,14 @@ class ViewController: UIViewController { @IBAction func presentFromButton(_ sender: UIButton) { resetFDTakeController() fdTakeController.presentingView = sender + fdTakeController.presentingRect = self.view.frame fdTakeController.present() } @IBAction func presentFromWindow() { resetFDTakeController() fdTakeController.presentingView = self.view + fdTakeController.presentingRect = self.view.frame fdTakeController.present() } }