Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Custom Crop ViewController #113

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions FDTake.podspec
Original file line number Diff line number Diff line change
@@ -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 cropping"
s.description = <<-DESC
`FDTake` helps you quickly have the user take or choose an existing photo or video.
DESC
Expand All @@ -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
30 changes: 24 additions & 6 deletions FDTake.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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, ); }; };
Expand All @@ -25,9 +27,11 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
D07FC15F218E31EE00310E7D /* ImageCropper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCropper.swift; sourceTree = "<group>"; };
D07FC160218E31EF00310E7D /* UIImage+FixOrientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+FixOrientation.swift"; sourceTree = "<group>"; };
D0BA674D2235B5BF003B30D9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
D95E1BE11D88D63300F94976 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
D95E1BED1D88D63400F94976 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -72,9 +76,18 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
D0BA674C2235B5BF003B30D9 /* Supporting Files */ = {
isa = PBXGroup;
children = (
D0BA674D2235B5BF003B30D9 /* Info.plist */,
);
path = "Supporting Files";
sourceTree = "<group>";
};
D95E1BD31D88D63200F94976 = {
isa = PBXGroup;
children = (
D0BA674C2235B5BF003B30D9 /* Supporting Files */,
D95E1BDF1D88D63300F94976 /* Source */,
D95E1BEA1D88D63400F94976 /* Tests */,
D95E1BDE1D88D63300F94976 /* Products */,
Expand All @@ -93,9 +106,10 @@
D95E1BDF1D88D63300F94976 /* Source */ = {
isa = PBXGroup;
children = (
D07FC15F218E31EE00310E7D /* ImageCropper.swift */,
D07FC160218E31EF00310E7D /* UIImage+FixOrientation.swift */,
D9D7F7B81D8DAC4F00A0DA5B /* FDTakeController.swift */,
D95E1BE01D88D63300F94976 /* FDTake.h */,
D95E1BE11D88D63300F94976 /* Info.plist */,
D983D7531D8DDA0700E4223A /* Localizable.strings */,
);
path = Source;
Expand Down Expand Up @@ -241,7 +255,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D07FC162218E31EF00310E7D /* UIImage+FixOrientation.swift in Sources */,
D9D7F7B91D8DAC4F00A0DA5B /* FDTakeController.swift in Sources */,
D07FC161218E31EF00310E7D /* ImageCropper.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -350,6 +366,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 = "";
Expand Down Expand Up @@ -405,6 +422,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";
Expand All @@ -421,10 +439,10 @@
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.--PROJECT-NAME--";
PRODUCT_BUNDLE_IDENTIFIER = net.phor.FDTake;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
Expand All @@ -441,10 +459,10 @@
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.--PROJECT-NAME--";
PRODUCT_BUNDLE_IDENTIFIER = net.phor.FDTake;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 4.2;
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
138 changes: 90 additions & 48 deletions Source/FDTakeController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]?) -> Void) {
let fdTake = FDTakeController()
fdTake.allowsVideo = false
fdTake.didGetPhoto = callback
Expand Down Expand Up @@ -109,11 +109,12 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa
return UIApplication.shared.keyWindow!.rootViewController!
}()

open var aspectRatio: CGFloat = 4/3

// MARK: - Callbacks

/// A photo was selected
open var didGetPhoto: ((_ photo: UIImage, _ info: [AnyHashable: Any]) -> Void)?
open var didGetPhoto: ((_ photo: UIImage, _ info: [AnyHashable: Any]?) -> Void)?

/// A video was selected
open var didGetVideo: ((_ video: URL, _ info: [AnyHashable: Any]) -> Void)?
Expand Down Expand Up @@ -149,15 +150,16 @@ open class FDTakeController: NSObject /* , UIImagePickerControllerDelegate, UINa
open var takeVideoText: String? = nil


// MARK: - Private
internal var info: [AnyHashable: Any]?

internal lazy var imagePicker: UIImagePickerController = {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = false
return picker
}()

private lazy var imagePicker: UIImagePickerController = {
[unowned self] in
let retval = UIImagePickerController()
retval.delegate = self
retval.allowsEditing = true
return retval
}()
// MARK: - Private

private var alertController: UIAlertController? = nil

Expand Down Expand Up @@ -206,6 +208,38 @@ 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!.sourceView = self.presentingView!
self.imagePicker.popoverPresentationController!.permittedArrowDirections = .any
}

}

/// Presents the user with an option to take a photo or choose a photo from the library
open func present() {
Expand Down Expand Up @@ -239,7 +273,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 = UIWindow.Level.alert + 1
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
return
Expand All @@ -254,36 +288,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)
}
Expand Down Expand Up @@ -315,10 +322,10 @@ 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:[UIImagePickerController.InfoKey : 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
var imageToSave: UIImage
Expand All @@ -332,15 +339,37 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro
self.didCancel?()
return
}
self.didGetPhoto?(imageToSave, info)
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[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaURL)] as! URL, info)
}

picker.dismiss(animated: true, completion: nil)
}

/// Conformance for image picker delegate
Expand All @@ -349,15 +378,28 @@ extension FDTakeController : UIImagePickerControllerDelegate, UINavigationContro
picker.dismiss(animated: true, completion: nil)
self.didDeny?()
}

// 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
}

}

extension FDTakeController: UIImageCropperProtocol {
public func didCropImage(originalImage: UIImage?, croppedImage: UIImage?) {

if let image = croppedImage {
self.didGetPhoto?(image, self.info)
} else {
self.didFail?()
}
self.dismiss()
}

}