mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Add view to Crop and scale avatar.
// FREEBIE
This commit is contained in:
parent
428edb6172
commit
2c301feeb7
6 changed files with 851 additions and 6 deletions
|
@ -33,6 +33,8 @@
|
|||
3453D8EA1EC0D4ED003F9E6F /* OWSAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3453D8E91EC0D4ED003F9E6F /* OWSAlerts.swift */; };
|
||||
345671011E89A5F1006EE662 /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671001E89A5F1006EE662 /* ThreadUtil.m */; };
|
||||
3456710A1E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671091E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m */; };
|
||||
346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */; };
|
||||
346B66331F4F08FD00E5122F /* IMG_4187.PNG in Resources */ = {isa = PBXBuildFile; fileRef = 346B66321F4F08FD00E5122F /* IMG_4187.PNG */; };
|
||||
3471B1DA1EB7C63600F6AEC8 /* NewNonContactConversationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3471B1D91EB7C63600F6AEC8 /* NewNonContactConversationViewController.m */; };
|
||||
3472229F1EB22FFE00E53955 /* AddToGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3472229E1EB22FFE00E53955 /* AddToGroupViewController.m */; };
|
||||
348F2EAE1F0D21BC00D4ECE0 /* DeviceSleepManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */; };
|
||||
|
@ -439,6 +441,8 @@
|
|||
345671001E89A5F1006EE662 /* ThreadUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadUtil.m; sourceTree = "<group>"; };
|
||||
345671081E8A9F5D006EE662 /* TSGenericAttachmentAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGenericAttachmentAdapter.h; sourceTree = "<group>"; };
|
||||
345671091E8A9F5D006EE662 /* TSGenericAttachmentAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGenericAttachmentAdapter.m; sourceTree = "<group>"; };
|
||||
346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CropScaleImageViewController.swift; sourceTree = "<group>"; };
|
||||
346B66321F4F08FD00E5122F /* IMG_4187.PNG */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = IMG_4187.PNG; sourceTree = "<group>"; };
|
||||
3471B1D81EB7C63600F6AEC8 /* NewNonContactConversationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewNonContactConversationViewController.h; sourceTree = "<group>"; };
|
||||
3471B1D91EB7C63600F6AEC8 /* NewNonContactConversationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewNonContactConversationViewController.m; sourceTree = "<group>"; };
|
||||
3472229D1EB22FFE00E53955 /* AddToGroupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToGroupViewController.h; sourceTree = "<group>"; };
|
||||
|
@ -1000,6 +1004,7 @@
|
|||
3448BFC01EDF0EA7005B2D69 /* ConversationView */,
|
||||
34B3F8401E8DF1700035BE1A /* CountryCodeViewController.h */,
|
||||
34B3F8411E8DF1700035BE1A /* CountryCodeViewController.m */,
|
||||
346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */,
|
||||
34D8C0221ED3673300188D7C /* DebugUI */,
|
||||
3497DBED1ECE2E4700DB2605 /* DomainFrontingCountryViewController.h */,
|
||||
3497DBEE1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m */,
|
||||
|
@ -1555,6 +1560,7 @@
|
|||
AD83FF381A73426500B5C81A /* audio_pause_button_blue.png */,
|
||||
AD83FF391A73426500B5C81A /* audio_pause_button_blue@2x.png */,
|
||||
AD83FF3A1A73426500B5C81A /* audio_play_button_blue@2x.png */,
|
||||
346B66321F4F08FD00E5122F /* IMG_4187.PNG */,
|
||||
AD83FF3B1A73426500B5C81A /* audio_play_button.png */,
|
||||
AD83FF3C1A73426500B5C81A /* audio_play_button@2x.png */,
|
||||
AD83FF3D1A73426500B5C81A /* audio_pause_button.png */,
|
||||
|
@ -1974,6 +1980,7 @@
|
|||
34330A5C1E787A9800DF2FB9 /* dripicons-v2.ttf in Resources */,
|
||||
B633C5C41A1D190B0059AC12 /* mute_on@2x.png in Resources */,
|
||||
B633C5CE1A1D190B0059AC12 /* quit@2x.png in Resources */,
|
||||
346B66331F4F08FD00E5122F /* IMG_4187.PNG in Resources */,
|
||||
AD83FF441A73426500B5C81A /* audio_pause_button.png in Resources */,
|
||||
B6F509971AA53F760068F56A /* Localizable.strings in Resources */,
|
||||
AD41D7B51A6F6F0600241130 /* play_button.png in Resources */,
|
||||
|
@ -2372,6 +2379,7 @@
|
|||
34C42D661F4734ED0072EC04 /* OWSContactOffersInteraction.m in Sources */,
|
||||
34B3F8941E8DF1710035BE1A /* SignalsViewController.m in Sources */,
|
||||
34E8BF381EE9E2FD00F5F4CA /* FingerprintViewScanController.m in Sources */,
|
||||
346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */,
|
||||
76EB058818170B33006006FC /* PropertyListPreferences.m in Sources */,
|
||||
34330A611E788EA900DF2FB9 /* AttachmentUploadView.m in Sources */,
|
||||
34B3F87D1E8DF1700035BE1A /* FullImageViewController.m in Sources */,
|
||||
|
@ -2564,7 +2572,11 @@
|
|||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = "DEBUG=1 $(inherited) SSK_BUILDING_FOR_TESTS=1";
|
||||
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
"SSK_BUILDING_FOR_TESTS=1",
|
||||
);
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
|
BIN
Signal/Images/IMG_4187.PNG
Normal file
BIN
Signal/Images/IMG_4187.PNG
Normal file
Binary file not shown.
After Width: | Height: | Size: 235 KiB |
|
@ -98,6 +98,28 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
- (void)showCropScaleUI
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
OWSAssert(self.delegate);
|
||||
|
||||
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
|
||||
picker.delegate = self;
|
||||
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
|
||||
|
||||
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {
|
||||
picker.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeImage, nil];
|
||||
[self.delegate.fromViewController presentViewController:picker
|
||||
animated:YES
|
||||
completion:[UIUtil modalCompletionBlock]];
|
||||
}
|
||||
}
|
||||
//// We resize the avatar to fill a 210x210 square.
|
||||
////
|
||||
//// See: GroupCreateActivity.java in Signal-Android.java.
|
||||
// UIImage *resizedAvatar = [rawAvatar resizedImageToFillPixelSize:CGSizeMake(210, 210)];
|
||||
//[self.delegate avatarDidChange:resizedAvatar];
|
||||
|
||||
/*
|
||||
* Dismissing UIImagePickerController
|
||||
*/
|
||||
|
@ -121,11 +143,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
UIImage *rawAvatar = [info objectForKey:UIImagePickerControllerOriginalImage];
|
||||
|
||||
if (rawAvatar) {
|
||||
// We resize the avatar to fill a 210x210 square.
|
||||
//
|
||||
// See: GroupCreateActivity.java in Signal-Android.java.
|
||||
UIImage *resizedAvatar = [rawAvatar resizedImageToFillPixelSize:CGSizeMake(210, 210)];
|
||||
[self.delegate avatarDidChange:resizedAvatar];
|
||||
// [self showCropScaleUI:rawAvatar];
|
||||
}
|
||||
|
||||
[self.delegate.fromViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
|
|
798
Signal/src/ViewControllers/CropScaleImageViewController.swift
Normal file
798
Signal/src/ViewControllers/CropScaleImageViewController.swift
Normal file
|
@ -0,0 +1,798 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MediaPlayer
|
||||
|
||||
class OWSLayerView: UIView {
|
||||
var layoutCallback : (() -> Void)?
|
||||
|
||||
required init(frame: CGRect, layoutCallback : @escaping () -> Void) {
|
||||
self.layoutCallback = layoutCallback
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.layoutCallback = {
|
||||
}
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override var bounds: CGRect {
|
||||
didSet {
|
||||
guard let layoutCallback = self.layoutCallback else {
|
||||
return
|
||||
}
|
||||
layoutCallback()
|
||||
}
|
||||
}
|
||||
|
||||
override var frame: CGRect {
|
||||
didSet {
|
||||
guard let layoutCallback = self.layoutCallback else {
|
||||
return
|
||||
}
|
||||
layoutCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CropScaleImageViewController: OWSViewController {
|
||||
|
||||
let TAG = "[CropScaleImageViewController]"
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
let srcImage: UIImage
|
||||
|
||||
var successCompletion: ((UIImage) -> Void)?
|
||||
|
||||
var imageView: UIView?
|
||||
|
||||
var imageLayer: CALayer?
|
||||
|
||||
var dashedBorderLayer: CAShapeLayer?
|
||||
|
||||
// var defaultCropFramePoints: CGRect?
|
||||
// var currentCropFramePoints: CGRect?
|
||||
|
||||
// In width/height.
|
||||
let targetAspectRatio: CGFloat = 1.0
|
||||
|
||||
var srcImageSizePoints: CGSize = CGSize.zero
|
||||
var unitDefaultCropSizePoints: CGSize = CGSize.zero
|
||||
// var unitDefaultCropFramePoints : CGRect = CGRect.zero
|
||||
// coordinate
|
||||
// var maxUnitTranslation : CGPoint = CGPoint.zero
|
||||
|
||||
// N = Scaled, zoomed in.
|
||||
let kMaxImageScale: CGFloat = 4.0
|
||||
// 1.0 = Unscaled, cropped to fill crop rect.
|
||||
let kMinImageScale: CGFloat = 1.0
|
||||
var imageScale: CGFloat = 1.0
|
||||
|
||||
// 0
|
||||
// var imageTranslation : CGPoint = CGPoint.zero
|
||||
var srcTranslation: CGPoint = CGPoint.zero
|
||||
// var maxImageTranslation : CGPoint = CGPoint.zero
|
||||
|
||||
//
|
||||
// var imageScale : CGFloat = kMinImageScale
|
||||
// var imageTranslation : CGPoint = CGPoint.zero
|
||||
|
||||
// var videoPlayer: MPMoviePlayerController?
|
||||
//
|
||||
// var audioPlayer: OWSAudioAttachmentPlayer?
|
||||
// var audioStatusLabel: UILabel?
|
||||
// var audioPlayButton: UIButton?
|
||||
// var isAudioPlayingFlag = false
|
||||
// var isAudioPaused = false
|
||||
// var audioProgressSeconds: CGFloat = 0
|
||||
// var audioDurationSeconds: CGFloat = 0
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
@available(*, unavailable, message:"use attachment: constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.srcImage = UIImage(named:"fail")!
|
||||
super.init(coder: aDecoder)
|
||||
owsFail("\(self.TAG) invalid constructor")
|
||||
|
||||
configureCropAndScale()
|
||||
}
|
||||
|
||||
required init(srcImage: UIImage, successCompletion : @escaping (UIImage) -> Void) {
|
||||
self.srcImage = srcImage
|
||||
self.successCompletion = successCompletion
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
configureCropAndScale()
|
||||
}
|
||||
|
||||
// MARK: Cropping and Scaling
|
||||
|
||||
private func configureCropAndScale() {
|
||||
// Size of bounding box that reflects the target aspect ratio, whose longer side = 1.
|
||||
let unitSquareHeight: CGFloat = (targetAspectRatio >= 1.0 ? 1.0 : 1.0 / targetAspectRatio)
|
||||
let unitSquareWidth: CGFloat = (targetAspectRatio >= 1.0 ? targetAspectRatio * unitSquareHeight : 1.0)
|
||||
let unitSquareSize = CGSize(width: unitSquareWidth, height: unitSquareHeight)
|
||||
|
||||
let imageSizePoints = srcImage.size
|
||||
guard
|
||||
(imageSizePoints.width > 0 && imageSizePoints.height > 0) else {
|
||||
return
|
||||
}
|
||||
self.srcImageSizePoints = imageSizePoints
|
||||
|
||||
Logger.error("----")
|
||||
Logger.error("imageSizePoints: \(imageSizePoints)")
|
||||
Logger.error("unitSquareWidth: \(unitSquareWidth)")
|
||||
Logger.error("unitSquareHeight: \(unitSquareHeight)")
|
||||
|
||||
// Default
|
||||
|
||||
// The "default" (no scaling, no translation) crop frame, expressed in
|
||||
// srcImage's coordinate system.
|
||||
unitDefaultCropSizePoints = defaultCropSizePoints(dstSizePoints:unitSquareSize)
|
||||
// unitDefaultCropFramePoints = defaultCropFramePoints(dstSizePoints:unitSquareSize)
|
||||
assert(imageSizePoints.width >= unitDefaultCropSizePoints.width)
|
||||
assert(imageSizePoints.height >= unitDefaultCropSizePoints.height)
|
||||
|
||||
// maxUnitTranslation = CGPoint(x:
|
||||
Logger.error("unitDefaultCropSizePoints: \(unitDefaultCropSizePoints)")
|
||||
srcTranslation = CGPoint(x:(imageSizePoints.width - unitDefaultCropSizePoints.width) * 0.5,
|
||||
y:(imageSizePoints.height - unitDefaultCropSizePoints.height) * 0.5)
|
||||
Logger.error("srcTranslation: \(srcTranslation)")
|
||||
// let maxSrcTranslation = CGPoint(x:(imageSizePoints.width - unitDefaultCropSizePoints.width) * 0.5,
|
||||
// y:(imageSizePoints.height - unitDefaultCropSizePoints.height) * 0.5)
|
||||
// srcTranslation =
|
||||
|
||||
// self.defaultCropFramePoints = defaultCropFramePoints
|
||||
// let maxCropSizePoints = CGSize(width:defaultCropFramePoints.width,
|
||||
// height:defaultCropFramePoints.height)
|
||||
// let minCropSizePoints = CGSize(width:defaultCropFramePoints.width / CropScaleImageViewController.kMaxImageScale,
|
||||
// height:defaultCropFramePoints.height / CropScaleImageViewController.kMaxImageScale)
|
||||
// Logger.error("defaultCropFramePoints: \(defaultCropFramePoints)")
|
||||
// Logger.error("maxCropSizePoints: \(maxCropSizePoints)")
|
||||
// Logger.error("minCropSizePoints: \(minCropSizePoints)")
|
||||
//
|
||||
// if currentCropFramePoints == nil {
|
||||
// currentCropFramePoints = defaultCropFramePoints
|
||||
// }
|
||||
// var cropFramePoints = currentCropFramePoints!
|
||||
}
|
||||
|
||||
private func defaultCropSizePoints(dstSizePoints: CGSize) -> (CGSize) {
|
||||
assert(srcImageSizePoints.width > 0)
|
||||
assert(srcImageSizePoints.height > 0)
|
||||
|
||||
let imageAspectRatio = srcImageSizePoints.width / srcImageSizePoints.height
|
||||
let dstAspectRatio = dstSizePoints.width / dstSizePoints.height
|
||||
|
||||
var dstCropSizePoints = CGSize.zero
|
||||
if imageAspectRatio > dstAspectRatio {
|
||||
dstCropSizePoints = CGSize(width: dstSizePoints.width / dstSizePoints.height * srcImageSizePoints.height, height: srcImageSizePoints.height)
|
||||
} else {
|
||||
dstCropSizePoints = CGSize(width: srcImageSizePoints.width, height: dstSizePoints.height / dstSizePoints.width * srcImageSizePoints.width)
|
||||
}
|
||||
return dstCropSizePoints
|
||||
}
|
||||
|
||||
// private func defaultCropFramePoints(dstSizePoints: CGSize) -> (CGRect) {
|
||||
// assert(imageSizePoints.width > 0)
|
||||
// assert(imageSizePoints.height > 0)
|
||||
//
|
||||
// let imageAspectRatio = imageSizePoints.width / imageSizePoints.height
|
||||
// let dstAspectRatio = dstSizePoints.width / dstSizePoints.height
|
||||
//
|
||||
// var dstCropSizePoints = CGSize.zero
|
||||
// if imageAspectRatio > dstAspectRatio {
|
||||
// dstCropSizePoints = CGSize(width: dstSizePoints.width / dstSizePoints.height * imageSizePoints.height, height: imageSizePoints.height)
|
||||
// } else {
|
||||
// dstCropSizePoints = CGSize(width: imageSizePoints.width, height: dstSizePoints.height / dstSizePoints.width * imageSizePoints.width)
|
||||
// }
|
||||
//
|
||||
// let dstCropOriginPoints = CGPoint.zero
|
||||
// assert(dstCropOriginPoints.x >= 0)
|
||||
// assert(dstCropOriginPoints.y >= 0)
|
||||
// assert(dstCropOriginPoints.x <= dstSizePoints.width - dstCropSizePoints.width)
|
||||
// assert(dstCropOriginPoints.y <= dstSizePoints.height - dstCropSizePoints.height)
|
||||
// return CGRect(origin:dstCropOriginPoints, size:dstCropSizePoints)
|
||||
// }
|
||||
//
|
||||
// MARK: View Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = UIColor.white
|
||||
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem:.stop,
|
||||
target:self,
|
||||
action:#selector(cancelPressed))
|
||||
self.navigationItem.title = NSLocalizedString("CROP_SCALE_IMAGE_VIEW_TITLE",
|
||||
comment: "Title for the 'crop/scale image' dialog.")
|
||||
|
||||
createViews()
|
||||
}
|
||||
|
||||
// override func viewWillAppear(_ animated: Bool) {
|
||||
// super.viewWillAppear(animated)
|
||||
//
|
||||
// ViewControllerUtils.setAudioIgnoresHardwareMuteSwitch(true)
|
||||
// }
|
||||
//
|
||||
// override func viewWillDisappear(_ animated: Bool) {
|
||||
// super.viewWillDisappear(animated)
|
||||
//
|
||||
// ViewControllerUtils.setAudioIgnoresHardwareMuteSwitch(false)
|
||||
// }
|
||||
|
||||
// MARK: - Create Views
|
||||
|
||||
private func createViews() {
|
||||
let previewTopMargin: CGFloat = 30
|
||||
let previewHMargin: CGFloat = 20
|
||||
|
||||
let contentView = UIView()
|
||||
self.view.addSubview(contentView)
|
||||
contentView.autoPinWidthToSuperview(withMargin:previewHMargin)
|
||||
contentView.autoPin(toTopLayoutGuideOf: self, withInset:previewTopMargin)
|
||||
|
||||
createButtonRow(contentView:contentView)
|
||||
|
||||
let imageHMargin: CGFloat = 0
|
||||
let imageView = OWSLayerView(frame:CGRect.zero, layoutCallback: {[weak self] _ in
|
||||
guard let strongSelf = self else { return }
|
||||
strongSelf.updateImageLayout()
|
||||
})
|
||||
imageView.clipsToBounds = true
|
||||
self.imageView = imageView
|
||||
contentView.addSubview(imageView)
|
||||
imageView.autoPinWidthToSuperview(withMargin:imageHMargin)
|
||||
imageView.autoVCenterInSuperview()
|
||||
imageView.autoPinToSquareAspectRatio()
|
||||
|
||||
let imageLayer = CALayer()
|
||||
self.imageLayer = imageLayer
|
||||
imageLayer.contents = srcImage.cgImage
|
||||
imageView.layer.addSublayer(imageLayer)
|
||||
|
||||
let dashedBorderLayer = CAShapeLayer()
|
||||
self.dashedBorderLayer = dashedBorderLayer
|
||||
dashedBorderLayer.strokeColor = UIColor.ows_materialBlue().cgColor
|
||||
dashedBorderLayer.lineDashPattern = [6, 6]
|
||||
dashedBorderLayer.lineWidth = 2
|
||||
dashedBorderLayer.fillColor = nil
|
||||
imageView.layer.addSublayer(dashedBorderLayer)
|
||||
|
||||
contentView.isUserInteractionEnabled = true
|
||||
contentView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(sender:))))
|
||||
contentView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan(sender:))))
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
updateImageLayout()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.view.layoutSubviews()
|
||||
updateImageLayout()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
updateImageLayout()
|
||||
}
|
||||
|
||||
// private func imageSizeAndViewSizePoints(imageView: UIView) -> (CGSize?, CGSize?) {
|
||||
// let imageSizePoints = srcImage.size
|
||||
// guard
|
||||
// (imageSizePoints.width > 0 && imageSizePoints.height > 0) else {
|
||||
// return (nil, nil)
|
||||
// }
|
||||
//
|
||||
// let viewSizePoints = imageView.frame.size
|
||||
// guard
|
||||
// (viewSizePoints.width > 0 && viewSizePoints.height > 0) else {
|
||||
// return (nil, nil)
|
||||
// }
|
||||
//
|
||||
// return (imageSizePoints, viewSizePoints)
|
||||
// }
|
||||
|
||||
private func defaultCropFramePoints(imageSizePoints: CGSize, viewSizePoints: CGSize) -> (CGRect) {
|
||||
let imageAspectRatio = imageSizePoints.width / imageSizePoints.height
|
||||
let viewAspectRatio = viewSizePoints.width / viewSizePoints.height
|
||||
|
||||
var defaultCropSizePoints = CGSize.zero
|
||||
if imageAspectRatio > viewAspectRatio {
|
||||
defaultCropSizePoints = CGSize(width: viewSizePoints.width / viewSizePoints.height * imageSizePoints.height, height: imageSizePoints.height)
|
||||
} else {
|
||||
defaultCropSizePoints = CGSize(width: imageSizePoints.width, height: viewSizePoints.height / viewSizePoints.width * imageSizePoints.width)
|
||||
}
|
||||
|
||||
let defaultCropOriginPoints = CGPoint(x: (imageSizePoints.width - defaultCropSizePoints.width) * 0.5,
|
||||
y: (imageSizePoints.height - defaultCropSizePoints.height) * 0.5)
|
||||
assert(defaultCropOriginPoints.x >= 0)
|
||||
assert(defaultCropOriginPoints.y >= 0)
|
||||
assert(defaultCropOriginPoints.x <= imageSizePoints.width - defaultCropSizePoints.width)
|
||||
assert(defaultCropOriginPoints.y <= imageSizePoints.height - defaultCropSizePoints.height)
|
||||
return CGRect(origin:defaultCropOriginPoints, size:defaultCropSizePoints)
|
||||
}
|
||||
|
||||
// private func imageBaseSizeAndOffset(imageSize:CGSize,viewSize:CGSize) -> (CGSize, CGPoint)
|
||||
// {
|
||||
// let imageAspectRatio = imageSize.width / imageSize.height
|
||||
// let viewAspectRatio = viewSize.width / viewSize.height
|
||||
//
|
||||
// var imageBaseSize = CGSize.zero
|
||||
// if imageAspectRatio > viewAspectRatio {
|
||||
// imageBaseSize = CGSize(width: imageSize.width / imageSize.height * viewSize.height, height: viewSize.height)
|
||||
// } else {
|
||||
// imageBaseSize = CGSize(width: viewSize.width, height: imageSize.height / imageSize.width * viewSize.width)
|
||||
// }
|
||||
//
|
||||
// let imageBaseOffset = CGPoint(x: (imageBaseSize.width - viewSize.width) * -0.5,
|
||||
// y: (imageBaseSize.height - viewSize.height) * -0.5)
|
||||
//
|
||||
// return (imageBaseSize, imageBaseOffset)
|
||||
// }
|
||||
|
||||
private func updateImageLayout() {
|
||||
guard let imageView = self.imageView else {
|
||||
return
|
||||
}
|
||||
guard let imageLayer = self.imageLayer else {
|
||||
return
|
||||
}
|
||||
guard let dashedBorderLayer = self.dashedBorderLayer else {
|
||||
return
|
||||
}
|
||||
guard srcImageSizePoints.width > 0 && srcImageSizePoints.height > 0 else {
|
||||
return
|
||||
}
|
||||
guard unitDefaultCropSizePoints.width > 0 && unitDefaultCropSizePoints.height > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
// var imageSizePoints : CGSize = CGSize.zero
|
||||
// var unitDefaultCropSizePoints : CGSize = CGSize.zero
|
||||
|
||||
// let imageSizePoints = srcImage.size
|
||||
// guard
|
||||
// (imageSizePoints.width > 0 && imageSizePoints.height > 0) else {
|
||||
// return
|
||||
// }
|
||||
|
||||
let viewSizePoints = imageView.frame.size
|
||||
guard
|
||||
(viewSizePoints.width > 0 && viewSizePoints.height > 0) else {
|
||||
return
|
||||
}
|
||||
|
||||
// let (srcSizePointsOptional, viewSizePointsOptional) = imageSizeAndViewSizePoints(imageView:imageView)
|
||||
// guard let srcSizePoints = srcSizePointsOptional else {
|
||||
// return
|
||||
// }
|
||||
// guard let viewSizePoints = viewSizePointsOptional else {
|
||||
// return
|
||||
// }
|
||||
Logger.error("----")
|
||||
Logger.error("srcImageSizePoints: \(srcImageSizePoints)")
|
||||
Logger.error("viewSizePoints: \(viewSizePoints)")
|
||||
|
||||
// let viewDefaultCropSizePoints = defaultCropSizePoints(dstSizePoints:viewSizePoints)
|
||||
// assert(viewDefaultCropSizePoints.width >= unitDefaultCropSizePoints.width)
|
||||
// assert(viewDefaultCropSizePoints.height >= unitDefaultCropSizePoints.height)
|
||||
//
|
||||
// Logger.error("viewDefaultCropSizePoints: \(viewDefaultCropSizePoints)")
|
||||
|
||||
Logger.error("imageScale: \(imageScale)")
|
||||
imageScale = max(kMinImageScale, min(kMaxImageScale, imageScale))
|
||||
Logger.error("imageScale (normalized): \(imageScale)")
|
||||
|
||||
let srcCropSizePoints = CGSize(width:unitDefaultCropSizePoints.width / imageScale,
|
||||
height:unitDefaultCropSizePoints.height / imageScale)
|
||||
|
||||
Logger.error("srcCropSizePoints: \(srcCropSizePoints)")
|
||||
|
||||
let minSrcTranslationPoints = CGPoint.zero
|
||||
let maxSrcTranslationPoints = CGPoint(x:srcImageSizePoints.width - srcCropSizePoints.width,
|
||||
y:srcImageSizePoints.height - srcCropSizePoints.height
|
||||
)
|
||||
|
||||
Logger.error("minSrcTranslationPoints: \(minSrcTranslationPoints)")
|
||||
Logger.error("maxSrcTranslationPoints: \(maxSrcTranslationPoints)")
|
||||
|
||||
Logger.error("srcTranslation: \(srcTranslation)")
|
||||
srcTranslation = CGPoint(x: max(minSrcTranslationPoints.x, min(maxSrcTranslationPoints.x, srcTranslation.x)),
|
||||
y: max(minSrcTranslationPoints.y, min(maxSrcTranslationPoints.y, srcTranslation.y)))
|
||||
Logger.error("srcTranslation (normalized): \(srcTranslation)")
|
||||
|
||||
let srcToViewRatio = viewSizePoints.width / srcCropSizePoints.width
|
||||
Logger.error("srcToViewRatio: \(srcToViewRatio)")
|
||||
|
||||
let imageViewFrame = CGRect(origin: CGPoint(x:srcTranslation.x * -srcToViewRatio,
|
||||
y:srcTranslation.y * -srcToViewRatio),
|
||||
size: CGSize(width:srcImageSizePoints.width * +srcToViewRatio,
|
||||
height:srcImageSizePoints.height * +srcToViewRatio
|
||||
))
|
||||
Logger.error("imageViewFrame: \(imageViewFrame)")
|
||||
imageLayer.removeAllAnimations()
|
||||
imageLayer.frame = imageViewFrame
|
||||
|
||||
// // maxUnitTranslation = CGPoint(x:
|
||||
// Logger.error("unitDefaultCropSizePoints: \(unitDefaultCropSizePoints)")
|
||||
// srcTranslation = CGPoint(x:(imageSizePoints.width - unitDefaultCropSizePoints.width) * 0.5,
|
||||
// y:(imageSizePoints.height - unitDefaultCropSizePoints.height) * 0.5)
|
||||
// Logger.error("srcTranslation: \(srcTranslation)")
|
||||
//
|
||||
//
|
||||
// // Default
|
||||
//
|
||||
// let defaultCropFramePoints = self.defaultCropFramePoints(imageSizePoints:imageSizePoints, viewSizePoints:viewSizePoints)
|
||||
// self.defaultCropFramePoints = defaultCropFramePoints
|
||||
// let maxCropSizePoints = CGSize(width:defaultCropFramePoints.width,
|
||||
// height:defaultCropFramePoints.height)
|
||||
// let minCropSizePoints = CGSize(width:defaultCropFramePoints.width / CropScaleImageViewController.kMaxImageScale,
|
||||
// height:defaultCropFramePoints.height / CropScaleImageViewController.kMaxImageScale)
|
||||
// Logger.error("defaultCropFramePoints: \(defaultCropFramePoints)")
|
||||
// Logger.error("maxCropSizePoints: \(maxCropSizePoints)")
|
||||
// Logger.error("minCropSizePoints: \(minCropSizePoints)")
|
||||
//
|
||||
// if currentCropFramePoints == nil {
|
||||
// currentCropFramePoints = defaultCropFramePoints
|
||||
// }
|
||||
// var cropFramePoints = currentCropFramePoints!
|
||||
//
|
||||
// // Ensure the crop frame has valid origin and size.0
|
||||
// cropFramePoints.size.width = max(minCropSizePoints.width, min(maxCropSizePoints.width, cropFramePoints.size.width))
|
||||
// cropFramePoints.size.height = max(minCropSizePoints.height, min(maxCropSizePoints.height, cropFramePoints.size.height))
|
||||
// let minCropOriginPoints = CGPoint.zero
|
||||
// let maxCropOriginPoints = CGPoint(x:imageSizePoints.width - cropFramePoints.size.width,
|
||||
// y:imageSizePoints.height - cropFramePoints.size.height
|
||||
// )
|
||||
// cropFramePoints.origin.x = max(minCropOriginPoints.x, min(maxCropOriginPoints.x, cropFramePoints.origin.x))
|
||||
// cropFramePoints.origin.y = max(minCropOriginPoints.y, min(maxCropOriginPoints.y, cropFramePoints.origin.y))
|
||||
//
|
||||
// // Update the property.
|
||||
// currentCropFramePoints = cropFramePoints
|
||||
// Logger.error("cropFramePoints: \(cropFramePoints)")
|
||||
//
|
||||
// let displayScaleWidth = viewSizePoints.width / cropFramePoints.width
|
||||
// let displayScaleHeight = viewSizePoints.height / cropFramePoints.height
|
||||
// let displayScale = (displayScaleWidth + displayScaleHeight) * 0.5
|
||||
// let displayFramePoints = CGRect(origin: CGPoint(x:cropFramePoints.origin.x * -displayScale, y:cropFramePoints.origin.y * -displayScale),
|
||||
// size: CGSize(width:imageSizePoints.width * displayScale,
|
||||
// height:imageSizePoints.height * displayScale))
|
||||
// Logger.error("displayScaleWidth: \(displayScaleWidth)")
|
||||
// Logger.error("displayScaleHeight: \(displayScaleHeight)")
|
||||
// Logger.error("displayScale: \(displayScale)")
|
||||
// Logger.error("displayFramePoints: \(displayFramePoints)")
|
||||
// imageLayer.frame = displayFramePoints
|
||||
// Logger.error("imageView: \(imageView.frame)")
|
||||
// Logger.error("imageLayer: \(displayFramePoints)")
|
||||
|
||||
//// let minCropFramePoints =
|
||||
//// static let kMaxImageScale : CGFloat = 4.0
|
||||
//// static let kMinImageScale : CGFloat = 1.0
|
||||
//
|
||||
// let (imageBaseSize, imageBaseOffset) = imageBaseSizeAndOffset(imageSize:imageSize, viewSize:viewSize)
|
||||
//
|
||||
// if cropFramePoints == nil || defaultCropFramePoints == nil {
|
||||
// defaultCropFramePoints =
|
||||
// CGRect(origin: imageBaseOffset, size: imageBaseSize)
|
||||
// cropFramePoints = defaultCropFramePoints
|
||||
// }
|
||||
//
|
||||
//// guard let imageView = self.imageView else {
|
||||
//// return
|
||||
//// }
|
||||
//// guard let imageLayer = self.imageLayer else {
|
||||
//// return
|
||||
//// }
|
||||
//// guard let dashedBorderLayer = self.dashedBorderLayer else {
|
||||
//// return
|
||||
//// }
|
||||
//
|
||||
//// let imageSize = srcImage.size
|
||||
//// guard
|
||||
//// (imageSize.width > 0 && imageSize.height > 0) else {
|
||||
//// return
|
||||
//// }
|
||||
////
|
||||
//// let viewSize = imageView.frame.size
|
||||
//// guard
|
||||
//// (viewSize.width > 0 && viewSize.height > 0) else {
|
||||
//// return
|
||||
//// }
|
||||
//
|
||||
// // Base
|
||||
//
|
||||
//// let imageAspectRatio = imageSize.width / imageSize.height
|
||||
//// let viewAspectRatio = viewSize.width / viewSize.height
|
||||
////
|
||||
//// var imageBaseSize = CGSize.zero
|
||||
//// if imageAspectRatio > viewAspectRatio {
|
||||
//// imageBaseSize = CGSize(width: imageSize.width / imageSize.height * viewSize.height, height: viewSize.height)
|
||||
//// } else {
|
||||
//// imageBaseSize = CGSize(width: viewSize.width, height: imageSize.height / imageSize.width * viewSize.width)
|
||||
//// }
|
||||
////
|
||||
//// let imageBaseOffset = CGPoint(x: (imageBaseSize.width - viewSize.width) * -0.5,
|
||||
//// y: (imageBaseSize.height - viewSize.height) * -0.5)
|
||||
//
|
||||
// // Display
|
||||
//
|
||||
//// assert(imageScale >= CropScaleImageViewController.kMinImageScale)
|
||||
//// assert(imageScale <= CropScaleImageViewController.kMaxImageScale)
|
||||
//
|
||||
//// static let kMaxImageScale : CGFloat = 4.0
|
||||
//// static let kMinImageScale : CGFloat = 1.0
|
||||
////
|
||||
//// var imageScale : CGFloat = kMinImageScale
|
||||
//
|
||||
// let imageDisplaySize = CGSize(width: imageBaseSize.width * imageScale,
|
||||
// height: imageBaseSize.height * imageScale)
|
||||
// let imageDisplayOffset = CGPoint(x: imageBaseOffset.x + imageTranslation.x * imageScale,
|
||||
// y: imageBaseOffset.y + imageTranslation.y * imageScale)
|
||||
// // TODO: Assert that imageDisplayOffset is valid.
|
||||
//// var imageScale : CGFloat = 1.0
|
||||
//// var imageTranslation : CGPoint = CGPoint.zero
|
||||
//
|
||||
// imageLayer.frame = CGRect(origin: imageDisplayOffset, size: imageDisplaySize)
|
||||
// Logger.error("imageView: \(NSStringFromCGRect(imageView.frame))")
|
||||
// Logger.error("imageLayer: \(imageLayer.frame)")
|
||||
|
||||
dashedBorderLayer.frame = imageView.bounds
|
||||
dashedBorderLayer.path = UIBezierPath(rect: imageView.bounds).cgPath
|
||||
}
|
||||
|
||||
var srcTranslationAtPinchStart: CGPoint = CGPoint.zero
|
||||
var imageScaleAtPinchStart: CGFloat = 0
|
||||
|
||||
// var currentCropFramePointsAtPinchStart: CGRect = CGRect.zero
|
||||
var lastPinchLocation: CGPoint = CGPoint.zero
|
||||
var lastPinchScale: CGFloat = 1.0
|
||||
// var isPinching = false
|
||||
|
||||
func handlePinch(sender: UIPinchGestureRecognizer) {
|
||||
Logger.error("pinch scale: \(sender.scale)")
|
||||
switch (sender.state) {
|
||||
case .possible:
|
||||
break
|
||||
case .began:
|
||||
srcTranslationAtPinchStart = srcTranslation
|
||||
imageScaleAtPinchStart = imageScale
|
||||
|
||||
// guard let currentCropFramePoints = currentCropFramePoints else {
|
||||
// isPinching = false
|
||||
// return
|
||||
// }
|
||||
// currentCropFramePointsAtPinchStart = currentCropFramePoints
|
||||
// isPinching = true
|
||||
lastPinchLocation =
|
||||
sender.location(in: sender.view)
|
||||
lastPinchScale = sender.scale
|
||||
break
|
||||
case .changed, .ended:
|
||||
guard let imageView = self.imageView else {
|
||||
return
|
||||
}
|
||||
// guard isPinching else {
|
||||
// return
|
||||
// }
|
||||
// guard let imageView = self.imageView else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let (_, viewSizePointsOptional) = imageSizeAndViewSizePoints(imageView:imageView)
|
||||
// guard let viewSizePoints = viewSizePointsOptional else {
|
||||
// return
|
||||
// }
|
||||
|
||||
// guard let imageView = self.imageView else {
|
||||
// return
|
||||
// }
|
||||
// let viewSizePoints = imageView.frame.size
|
||||
// Logger.error("viewSizePoints: \(viewSizePoints)")
|
||||
// let srcCropSizePoints = CGSize(width:unitDefaultCropSizePoints.width / imageScale,
|
||||
// height:unitDefaultCropSizePoints.height / imageScale)
|
||||
// Logger.error("srcCropSizePoints: \(srcCropSizePoints)")
|
||||
//
|
||||
// let srcToViewRatio = viewSizePoints.width / srcCropSizePoints.width
|
||||
|
||||
let location =
|
||||
sender.location(in: sender.view)
|
||||
let scaleDiff = sender.scale / lastPinchScale
|
||||
Logger.error("scaling \(lastPinchScale) \(sender.scale) -> \(scaleDiff)")
|
||||
|
||||
// unitDefaultCropSizePoints = defaultCropSizePoints(dstSizePoints:unitSquareSize)
|
||||
// // unitDefaultCropFramePoints = defaultCropFramePoints(dstSizePoints:unitSquareSize)
|
||||
// assert(imageSizePoints.width >= unitDefaultCropSizePoints.width)
|
||||
// assert(imageSizePoints.height >= unitDefaultCropSizePoints.height)
|
||||
|
||||
// maxUnitTranslation = CGPoint(x:
|
||||
// Logger.error("unitDefaultCropSizePoints: \(unitDefaultCropSizePoints)")
|
||||
// srcTranslation = CGPoint(x:(imageSizePoints.width - unitDefaultCropSizePoints.width) * 0.5,
|
||||
// y:(imageSizePoints.height - unitDefaultCropSizePoints.height) * 0.5)
|
||||
|
||||
// Update the scaling
|
||||
let srcCropSizeBeforeScalePoints = CGSize(width:unitDefaultCropSizePoints.width / imageScale,
|
||||
height:unitDefaultCropSizePoints.height / imageScale)
|
||||
imageScale = max(kMinImageScale, min(kMaxImageScale, imageScale * scaleDiff))
|
||||
let srcCropSizeAfterScalePoints = CGSize(width:unitDefaultCropSizePoints.width / imageScale,
|
||||
height:unitDefaultCropSizePoints.height / imageScale)
|
||||
// Since the translation state reflects the "upper left" corner of the crop region, we need to
|
||||
// adjust the translation when scaling.
|
||||
srcTranslation.x += (srcCropSizeBeforeScalePoints.width - srcCropSizeAfterScalePoints.width) * 0.5
|
||||
srcTranslation.y += (srcCropSizeBeforeScalePoints.height - srcCropSizeAfterScalePoints.height) * 0.5
|
||||
|
||||
// Update translation
|
||||
|
||||
let viewSizePoints = imageView.frame.size
|
||||
Logger.error("viewSizePoints: \(viewSizePoints)")
|
||||
let srcCropSizePoints = CGSize(width:unitDefaultCropSizePoints.width / imageScale,
|
||||
height:unitDefaultCropSizePoints.height / imageScale)
|
||||
Logger.error("srcCropSizePoints: \(srcCropSizePoints)")
|
||||
|
||||
let srcToViewRatio = viewSizePoints.width / srcCropSizePoints.width
|
||||
Logger.error("srcToViewRatio: \(srcToViewRatio)")
|
||||
let viewToSrcRatio = 1 / srcToViewRatio
|
||||
Logger.error("viewToSrcRatio: \(viewToSrcRatio)")
|
||||
|
||||
let gestureTranslation = CGPoint(x:location.x - lastPinchLocation.x,
|
||||
y:location.y - lastPinchLocation.y)
|
||||
|
||||
Logger.error("gestureTranslation: \(gestureTranslation)")
|
||||
|
||||
// var cropFramePoints = currentCropFramePointsAtPanStart
|
||||
// cropFramePoints.origin.x += +gestureTranslation.x / viewSizePoints.width * currentCropFramePointsAtPanStart.width
|
||||
// cropFramePoints.origin.y += -gestureTranslation.y / viewSizePoints.height * currentCropFramePointsAtPanStart.height
|
||||
// self.currentCropFramePoints = cropFramePoints
|
||||
|
||||
srcTranslation = CGPoint(x:srcTranslation.x + gestureTranslation.x * -viewToSrcRatio,
|
||||
y:srcTranslation.y + gestureTranslation.y * -viewToSrcRatio)
|
||||
|
||||
// let translationOffset = CGPoint(x:location.x - lastPinchLocation.x,
|
||||
// y:location.y - lastPinchLocation.y)
|
||||
// let oldCropFramePoints = self.currentCropFramePoints!
|
||||
// var newCropFramePoints = oldCropFramePoints
|
||||
// newCropFramePoints.size.width /= scaleDiff
|
||||
// newCropFramePoints.size.height /= scaleDiff
|
||||
// newCropFramePoints.origin.x += (oldCropFramePoints.size.width - newCropFramePoints.size.width) * 0.5
|
||||
// newCropFramePoints.origin.y += (oldCropFramePoints.size.height - newCropFramePoints.size.height) * 0.5
|
||||
//// cropFramePoints.origin.y += -gestureTranslation.y / viewSizePoints.height * currentCropFramePointsAtPinchStart.height
|
||||
//// cropFramePoints.origin.x += +gestureTranslation.x / viewSizePoints.width * currentCropFramePointsAtPinchStart.width
|
||||
//// cropFramePoints.origin.y += -gestureTranslation.y / viewSizePoints.height * currentCropFramePointsAtPinchStart.height
|
||||
// self.currentCropFramePoints = newCropFramePoints
|
||||
|
||||
lastPinchLocation = location
|
||||
lastPinchScale = sender.scale
|
||||
|
||||
// if sender.state == .ended {
|
||||
// isPinching = false
|
||||
// }
|
||||
break
|
||||
case .cancelled, .failed:
|
||||
srcTranslation = srcTranslationAtPinchStart
|
||||
imageScale = imageScaleAtPinchStart
|
||||
// guard isPinching else {
|
||||
// return
|
||||
// }
|
||||
// currentCropFramePoints
|
||||
// = currentCropFramePointsAtPinchStart
|
||||
// isPinching = false
|
||||
break
|
||||
}
|
||||
|
||||
updateImageLayout()
|
||||
}
|
||||
|
||||
var srcTranslationAtPanStart: CGPoint = CGPoint.zero
|
||||
|
||||
func handlePan(sender: UIPanGestureRecognizer) {
|
||||
switch (sender.state) {
|
||||
case .possible:
|
||||
break
|
||||
case .began:
|
||||
srcTranslationAtPanStart = srcTranslation
|
||||
break
|
||||
case .changed, .ended:
|
||||
guard let imageView = self.imageView else {
|
||||
return
|
||||
}
|
||||
let viewSizePoints = imageView.frame.size
|
||||
Logger.error("viewSizePoints: \(viewSizePoints)")
|
||||
let srcCropSizePoints = CGSize(width:unitDefaultCropSizePoints.width / imageScale,
|
||||
height:unitDefaultCropSizePoints.height / imageScale)
|
||||
Logger.error("srcCropSizePoints: \(srcCropSizePoints)")
|
||||
|
||||
let srcToViewRatio = viewSizePoints.width / srcCropSizePoints.width
|
||||
Logger.error("srcToViewRatio: \(srcToViewRatio)")
|
||||
let viewToSrcRatio = 1 / srcToViewRatio
|
||||
Logger.error("viewToSrcRatio: \(viewToSrcRatio)")
|
||||
|
||||
let gestureTranslation =
|
||||
sender.translation(in: sender.view)
|
||||
|
||||
Logger.error("gestureTranslation: \(gestureTranslation)")
|
||||
|
||||
// var cropFramePoints = currentCropFramePointsAtPanStart
|
||||
// cropFramePoints.origin.x += +gestureTranslation.x / viewSizePoints.width * currentCropFramePointsAtPanStart.width
|
||||
// cropFramePoints.origin.y += -gestureTranslation.y / viewSizePoints.height * currentCropFramePointsAtPanStart.height
|
||||
// self.currentCropFramePoints = cropFramePoints
|
||||
|
||||
srcTranslation = CGPoint(x:srcTranslationAtPanStart.x + gestureTranslation.x * -viewToSrcRatio,
|
||||
y:srcTranslationAtPanStart.y + gestureTranslation.y * -viewToSrcRatio)
|
||||
break
|
||||
case .cancelled, .failed:
|
||||
srcTranslation
|
||||
= srcTranslationAtPanStart
|
||||
break
|
||||
}
|
||||
|
||||
updateImageLayout()
|
||||
}
|
||||
|
||||
private func createButtonRow(contentView: UIView) {
|
||||
let buttonTopMargin = ScaleFromIPhone5To7Plus(30, 40)
|
||||
let buttonBottomMargin = ScaleFromIPhone5To7Plus(25, 40)
|
||||
|
||||
let buttonRow = UIView()
|
||||
self.view.addSubview(buttonRow)
|
||||
buttonRow.autoPinWidthToSuperview()
|
||||
buttonRow.autoPinEdge(toSuperviewEdge:.bottom, withInset:buttonBottomMargin)
|
||||
buttonRow.autoPinEdge(.top, to:.bottom, of:contentView, withOffset:buttonTopMargin)
|
||||
|
||||
let doneButton = createButton(title: NSLocalizedString("BUTTON_DONE",
|
||||
comment: "Label for generic done button."),
|
||||
color : UIColor.ows_materialBlue(),
|
||||
action: #selector(donePressed))
|
||||
buttonRow.addSubview(doneButton)
|
||||
doneButton.autoPinEdge(toSuperviewEdge:.top)
|
||||
doneButton.autoPinEdge(toSuperviewEdge:.bottom)
|
||||
doneButton.autoHCenterInSuperview()
|
||||
}
|
||||
|
||||
private func createButton(title: String, color: UIColor, action: Selector) -> UIButton {
|
||||
let buttonFont = UIFont.ows_mediumFont(withSize:ScaleFromIPhone5To7Plus(18, 22))
|
||||
let buttonCornerRadius = ScaleFromIPhone5To7Plus(4, 5)
|
||||
let buttonWidth = ScaleFromIPhone5To7Plus(110, 140)
|
||||
let buttonHeight = ScaleFromIPhone5To7Plus(35, 45)
|
||||
|
||||
let button = UIButton()
|
||||
button.setTitle(title, for:.normal)
|
||||
button.setTitleColor(UIColor.white, for:.normal)
|
||||
button.titleLabel!.font = buttonFont
|
||||
button.backgroundColor = color
|
||||
button.layer.cornerRadius = buttonCornerRadius
|
||||
button.clipsToBounds = true
|
||||
button.addTarget(self, action:action, for:.touchUpInside)
|
||||
button.autoSetDimension(.width, toSize:buttonWidth)
|
||||
button.autoSetDimension(.height, toSize:buttonHeight)
|
||||
return button
|
||||
}
|
||||
|
||||
// MARK: - Event Handlers
|
||||
|
||||
func cancelPressed(sender: UIButton) {
|
||||
dismiss(animated: true, completion:nil)
|
||||
}
|
||||
|
||||
func donePressed(sender: UIButton) {
|
||||
let successCompletion = self.successCompletion
|
||||
dismiss(animated: true, completion: {
|
||||
// TODO
|
||||
let dstImage = self.srcImage
|
||||
successCompletion?(dstImage)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -271,6 +271,17 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
}
|
||||
|
||||
[self updateBarButtonItems];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIImage *srcDmage = [UIImage imageNamed:@"IMG_4187.PNG"];
|
||||
OWSAssert(srcDmage);
|
||||
CropScaleImageViewController *vc =
|
||||
[[CropScaleImageViewController alloc] initWithSrcImage:srcDmage
|
||||
successCompletion:^(UIImage *_Nonnull dstImage){
|
||||
}];
|
||||
OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:vc];
|
||||
[self presentTopLevelModalViewController:navigationController animateDismissal:NO animatePresentation:YES];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateBarButtonItems {
|
||||
|
|
|
@ -205,6 +205,9 @@
|
|||
/* Title format for action sheet that offers to block an unknown user.Embeds {{the unknown user's name or phone number}}. */
|
||||
"BLOCK_OFFER_ACTIONSHEET_TITLE_FORMAT" = "Block %@?";
|
||||
|
||||
/* Label for generic done button. */
|
||||
"BUTTON_DONE" = "Done";
|
||||
|
||||
/* Alert message when calling and permissions for microphone are missing */
|
||||
"CALL_AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to make calls and record voice messages. You can grant this permission in the Settings app.";
|
||||
|
||||
|
@ -385,6 +388,9 @@
|
|||
/* Accessibility label for the create group new group button */
|
||||
"CREATE_NEW_GROUP" = "Create new group";
|
||||
|
||||
/* Title for the 'crop/scale image' dialog. */
|
||||
"CROP_SCALE_IMAGE_VIEW_TITLE" = "Crop Image";
|
||||
|
||||
/* Subtitle shown while the app is updating its database. */
|
||||
"DATABASE_VIEW_OVERLAY_SUBTITLE" = "This can take a few minutes.";
|
||||
|
||||
|
|
Loading…
Reference in a new issue