black masking style for avatar cropper

- cancel/done buttons
- no navbar for modal edit
- vertically center image

// FREEBIE
This commit is contained in:
Michael Kirk 2017-09-01 09:30:16 -04:00
parent 79f0c14e29
commit 4e93bec230
15 changed files with 98 additions and 86 deletions

View File

@ -13,6 +13,7 @@
#import "NotificationsManager.h"
#import "OWSAnyTouchGestureRecognizer.h"
#import "OWSAudioAttachmentPlayer.h"
#import "OWSBezierPathView.h"
#import "OWSCallNotificationsAdaptee.h"
#import "OWSContactAvatarBuilder.h"
#import "OWSContactsManager.h"

View File

@ -10,6 +10,8 @@ import Foundation
@objc class CommonStrings: NSObject {
static let dismissButton = NSLocalizedString("DISMISS_BUTTON_TEXT", comment: "Short text to dismiss current modal / actionsheet / screen")
static let cancelButton = NSLocalizedString("TXT_CANCEL_TITLE", comment:"Label for the cancel button in an alert or action sheet.")
}
@objc class CallStrings: NSObject {

View File

@ -355,8 +355,7 @@ class AttachmentApprovalViewController: OWSViewController, OWSAudioAttachmentPla
buttonSpacer.autoSetDimension(.width, toSize:buttonHSpacing)
buttonSpacer.autoHCenterInSuperview()
let cancelButton = createButton(title: NSLocalizedString("TXT_CANCEL_TITLE",
comment: ""),
let cancelButton = createButton(title: CommonStrings.cancelButton,
color : UIColor.ows_destructiveRed(),
action: #selector(cancelPressed))
buttonRow.addSubview(cancelButton)

View File

@ -132,10 +132,8 @@ NS_ASSUME_NONNULL_BEGIN
successCompletion:^(UIImage *_Nonnull dstImage) {
[self.delegate avatarDidChange:dstImage];
}];
OWSNavigationController *navigationController =
[[OWSNavigationController alloc] initWithRootViewController:vc];
[self.delegate.fromViewController
presentViewController:navigationController
presentViewController:vc
animated:YES
completion:[UIUtil modalCompletionBlock]];
}

View File

@ -5,6 +5,7 @@
#import "BlockListUIUtils.h"
#import "OWSContactsManager.h"
#import "PhoneNumber.h"
#import "Signal-Swift.h"
#import <SignalServiceKit/Contact.h>
#import <SignalServiceKit/OWSBlockingManager.h>
#import <SignalServiceKit/SignalAccount.h>
@ -104,7 +105,7 @@ typedef void (^BlockAlertCompletionBlock)();
}];
[actionSheetController addAction:unblockAction];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:CommonStrings.cancelButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *_Nonnull action) {
if (completionBlock) {
@ -208,7 +209,7 @@ typedef void (^BlockAlertCompletionBlock)();
}];
[actionSheetController addAction:unblockAction];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:CommonStrings.cancelButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *_Nonnull action) {
if (completionBlock) {

View File

@ -150,7 +150,7 @@ open class ContactsPicker: OWSViewController, UITableViewDelegate, UITableViewDa
let alert = UIAlertController(title: title, message: body, preferredStyle: UIAlertControllerStyle.alert)
let dismissText = NSLocalizedString("TXT_CANCEL_TITLE", comment:"")
let dismissText = CommonStrings.cancelButton
let cancelAction = UIAlertAction(title: dismissText, style: .cancel, handler: { _ in
let error = NSError(domain: "contactsPickerErrorDomain", code: 1, userInfo: [NSLocalizedDescriptionKey: "No Contacts Access"])

View File

@ -421,7 +421,7 @@ NS_ASSUME_NONNULL_BEGIN
contactViewController.allowsActions = NO;
contactViewController.allowsEditing = YES;
contactViewController.navigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
[[UIBarButtonItem alloc] initWithTitle:CommonStrings.cancelButton
style:UIBarButtonItemStylePlain
target:fromViewController
action:@selector(didFinishEditingContact)];

View File

@ -66,8 +66,6 @@ class CropScaleImageViewController: OWSViewController {
// We use a CALayer to render the image for performance reasons.
var imageLayer: CALayer!
var dashedBorderLayer: CAShapeLayer!
// In width/height.
//
// TODO: We could make this a parameter.
@ -98,6 +96,9 @@ class CropScaleImageViewController: OWSViewController {
// corner of the crop region in src image point coordinates.
var srcTranslation: CGPoint = CGPoint.zero
// space between the cropping circle and the outside edge of the view
let maskMargin = CGFloat(20)
// MARK: Initializers
@available(*, unavailable, message:"use srcImage:successCompletion: constructor instead.")
@ -174,29 +175,18 @@ class CropScaleImageViewController: OWSViewController {
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()
}
// MARK: - Create Views
private func createViews() {
let previewTopMargin: CGFloat = 30
let previewHMargin: CGFloat = 20
let contentView = UIView()
contentView.backgroundColor = UIColor.black
self.view.addSubview(contentView)
contentView.autoPinWidthToSuperview(withMargin:previewHMargin)
contentView.autoPin(toTopLayoutGuideOf: self, withInset:previewTopMargin)
contentView.autoPinEdgesToSuperviewEdges()
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()
@ -204,22 +194,45 @@ class CropScaleImageViewController: OWSViewController {
imageView.clipsToBounds = true
self.imageView = imageView
contentView.addSubview(imageView)
imageView.autoPinWidthToSuperview(withMargin:imageHMargin)
imageView.autoVCenterInSuperview()
imageView.autoPinToSquareAspectRatio()
imageView.autoPinEdgesToSuperviewEdges()
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 = [10, 10]
dashedBorderLayer.lineWidth = 4
dashedBorderLayer.fillColor = nil
imageView.layer.addSublayer(dashedBorderLayer)
let maskingView = OWSBezierPathView()
contentView.addSubview(maskingView)
maskingView.configureShapeLayerBlock = { layer, bounds in
let path = UIBezierPath(rect: bounds)
let radius = min(bounds.size.width, bounds.size.height) * 0.5 - self.maskMargin
// Center the circle's bounding rectangle
let circleRect = CGRect(x: bounds.size.width * 0.5 - radius, y: bounds.size.height * 0.5 - radius, width: radius * 2, height: radius * 2)
let circlePath = UIBezierPath(roundedRect: circleRect, cornerRadius: radius)
path.append(circlePath)
path.usesEvenOddFillRule = true
layer.path = path.cgPath
layer.fillRule = kCAFillRuleEvenOdd
layer.fillColor = UIColor.black.cgColor
layer.opacity = 0.7
}
maskingView.autoPinEdgesToSuperviewEdges()
let titleLabel = UILabel()
titleLabel.textColor = UIColor.white
titleLabel.textAlignment = .center
titleLabel.font = UIFont.ows_mediumFont(withSize:ScaleFromIPhone5(16))
titleLabel.text = NSLocalizedString("CROP_SCALE_IMAGE_VIEW_TITLE",
comment: "Title for the 'crop/scale image' dialog.")
contentView.addSubview(titleLabel)
titleLabel.autoPinWidthToSuperview()
let titleLabelMargin = ScaleFromIPhone5(16)
titleLabel.autoPin(toTopLayoutGuideOf:self, withInset:titleLabelMargin)
createButtonRow(contentView: contentView)
contentView.isUserInteractionEnabled = true
contentView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(sender:))))
@ -294,35 +307,36 @@ class CropScaleImageViewController: OWSViewController {
height:srcDefaultCropSizePoints.height / imageScale)
let minSrcTranslationPoints = CGPoint.zero
// Prevent panning outside of image area.
let maxSrcTranslationPoints = CGPoint(x:srcImageSizePoints.width - srcCropSizePoints.width,
y:srcImageSizePoints.height - srcCropSizePoints.height
)
// Normalize the translation property.
// Normalize the translation property
srcTranslation = CGPoint(x: max(minSrcTranslationPoints.x, min(maxSrcTranslationPoints.x, srcTranslation.x)),
y: max(minSrcTranslationPoints.y, min(maxSrcTranslationPoints.y, srcTranslation.y)))
let imageViewFrame = imageRenderRect(forDstSize:viewSizePoints)
var imageViewFrame = imageRenderRect(forDstSize: viewSizePoints)
// Disable implicit animations.
// offset to vertically center image in view (aspect ratio?)
let srcToViewRatio = viewSizePoints.width / srcCropSizePoints.width / imageScale
let heightOfImageLayer = srcDefaultCropSizePoints.height * srcToViewRatio
let yOffset = imageView.frame.height * 0.5 - heightOfImageLayer * 0.5
imageViewFrame = imageViewFrame.offsetBy(dx: 0, dy: yOffset)
// inset frame by the with of the masking margin so the user can pan to the very edge of the image
imageViewFrame = CGRect(x: imageViewFrame.origin.x + self.maskMargin,
y: imageViewFrame.origin.y + self.maskMargin,
width: imageViewFrame.width - maskMargin * 2,
height: imageViewFrame.height - maskMargin * 2)
// Disable implicit animations for snappier panning/zooming.
CATransaction.begin()
CATransaction.setDisableActions(true)
imageLayer.frame = imageViewFrame
// Mask to circle.
let maskLayer = CAShapeLayer()
maskLayer.frame = imageViewFrame
maskLayer.fillRule = kCAFillRuleEvenOdd
let maskFrame = CGRect(origin:CGPoint(x:-imageViewFrame.origin.x * 2,
y: -imageViewFrame.origin.y * 2),
size:imageView.bounds.size)
maskLayer.path =
CGPath(ellipseIn: maskFrame, transform: nil)
imageLayer.mask = maskLayer
dashedBorderLayer.frame = imageView.bounds
dashedBorderLayer.path = UIBezierPath(rect: imageView.bounds).cgPath
CATransaction.commit()
}
@ -333,10 +347,10 @@ class CropScaleImageViewController: OWSViewController {
let srcToViewRatio = dstSize.width / srcCropSizePoints.width
return CGRect(origin: CGPoint(x:srcTranslation.x * -srcToViewRatio,
y:srcTranslation.y * -srcToViewRatio),
size: CGSize(width:srcImageSizePoints.width * +srcToViewRatio,
height:srcImageSizePoints.height * +srcToViewRatio
return CGRect(origin: CGPoint(x: srcTranslation.x * -srcToViewRatio,
y: srcTranslation.y * -srcToViewRatio),
size: CGSize(width:srcImageSizePoints.width * +srcToViewRatio,
height:srcImageSizePoints.height * +srcToViewRatio
))
}
@ -442,19 +456,24 @@ class CropScaleImageViewController: OWSViewController {
buttonRow.autoPinEdge(toSuperviewEdge:.bottom, withInset:buttonBottomMargin)
buttonRow.autoPinEdge(.top, to:.bottom, of:contentView, withOffset:buttonTopMargin)
let cancelButton = createButton(title: CommonStrings.cancelButton,
action: #selector(cancelPressed))
buttonRow.addSubview(cancelButton)
cancelButton.autoPinEdge(toSuperviewEdge:.top)
cancelButton.autoPinEdge(toSuperviewEdge:.bottom)
cancelButton.autoPinEdge(toSuperviewEdge: .left)
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()
doneButton.autoPinEdge(toSuperviewEdge: .right)
}
private func createButton(title: String, color: UIColor, action: Selector) -> UIButton {
private func createButton(title: String, 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)
@ -462,9 +481,6 @@ class CropScaleImageViewController: OWSViewController {
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)
@ -497,8 +513,8 @@ class CropScaleImageViewController: OWSViewController {
let context = UIGraphicsGetCurrentContext()
context!.interpolationQuality = .high
let imageViewFrame = imageRenderRect(forDstSize:dstSizePixels)
srcImage.draw(in:imageViewFrame)
let imageViewFrame = imageRenderRect(forDstSize: dstSizePixels)
srcImage.draw(in: imageViewFrame)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
if scaledImage == nil {

View File

@ -7,6 +7,7 @@
#import "OWSDeviceProvisioningURLParser.h"
#import "OWSLinkedDevicesTableViewController.h"
#import "OWSProfileManager.h"
#import "Signal-Swift.h"
#import <SignalServiceKit/ECKeyPair+OWSPrivateKey.h>
#import <SignalServiceKit/OWSDeviceProvisioner.h>
#import <SignalServiceKit/OWSIdentityManager.h>
@ -80,15 +81,15 @@ NS_ASSUME_NONNULL_BEGIN
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
message:body
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction =
[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
});
}];
[UIAlertAction actionWithTitle:CommonStrings.cancelButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
});
}];
[alertController addAction:cancelAction];
UIAlertAction *proceedAction =
@ -111,7 +112,7 @@ NS_ASSUME_NONNULL_BEGIN
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction =
[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
[UIAlertAction actionWithTitle:CommonStrings.cancelButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
dispatch_async(dispatch_get_main_queue(), ^{
@ -183,7 +184,7 @@ NS_ASSUME_NONNULL_BEGIN
[alertController addAction:retryAction];
UIAlertAction *cancelAction =
[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
[UIAlertAction actionWithTitle:CommonStrings.cancelButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
dispatch_async(dispatch_get_main_queue(), ^{

View File

@ -319,7 +319,7 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien
message:NSLocalizedString(@"ALERT_VALIDATE_RECIPIENT_MESSAGE",
@"A message for the alert shown while validating a signal account")
preferredStyle:UIAlertControllerStyleAlert];
[activityAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")
[activityAlert addAction:[UIAlertAction actionWithTitle:CommonStrings.cancelButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *_Nonnull action) {
wasCancelled = YES;

View File

@ -85,7 +85,7 @@ NSString *const TSStorageManagerAppUpgradeNagDate = @"TSStorageManagerAppUpgrade
@"version number.}}.")];
[updater setAlertUpdateButtonTitle:NSLocalizedString(@"APP_UPDATE_NAG_ALERT_UPDATE_BUTTON",
@"Label for the 'update' button in the 'new app version available' alert.")];
[updater setAlertCancelButtonTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")];
[updater setAlertCancelButtonTitle:CommonStrings.cancelButton];
[updater setDelegate:self];
[updater showUpdateWithConfirmation];
}

View File

@ -41,7 +41,7 @@ import Foundation
}
public class func cancelAction() -> UIAlertAction {
let action = UIAlertAction(title: NSLocalizedString("TXT_CANCEL_TITLE", comment:"Label for the cancel button in an alert or action sheet."), style: .cancel) { _ in
let action = UIAlertAction(title: CommonStrings.cancelButton, style: .cancel) { _ in
Logger.debug("Cancel item")
// Do nothing.
}

View File

@ -9,12 +9,12 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSBezierPathView : UIView
// Configure the view with this method if it uses a single Bezier path.
- (void)setConfigureShapeLayerBlock:(ConfigureShapeLayerBlock)configureShapeLayerBlock;
@property (nonatomic) ConfigureShapeLayerBlock configureShapeLayerBlock;
// Configure the view with this method if it uses multiple Bezier paths.
//
// Paths will be rendered in back-to-front order.
- (void)setConfigureShapeLayerBlocks:(NSArray<ConfigureShapeLayerBlock> *)configureShapeLayerBlocks;
@property (nonatomic) NSArray<ConfigureShapeLayerBlock> *configureShapeLayerBlocks;
// This method forces the view to reconstruct its layer content. It shouldn't
// be necessary to call this unless the ConfigureShapeLayerBlocks depend on external

View File

@ -6,12 +6,6 @@
NS_ASSUME_NONNULL_BEGIN
@interface OWSBezierPathView ()
@property (nonatomic) NSArray<ConfigureShapeLayerBlock> *configureShapeLayerBlocks;
@end
#pragma mark -
@implementation OWSBezierPathView

View File

@ -389,7 +389,7 @@
"CREATE_NEW_GROUP" = "Create new group";
/* Title for the 'crop/scale image' dialog. */
"CROP_SCALE_IMAGE_VIEW_TITLE" = "Crop Image";
"CROP_SCALE_IMAGE_VIEW_TITLE" = "Crop and Scale";
/* Subtitle shown while the app is updating its database. */
"DATABASE_VIEW_OVERLAY_SUBTITLE" = "This can take a few minutes.";