Nav buttons: batch, camera/library switch, done

This commit is contained in:
Michael Kirk 2019-03-28 06:37:23 -06:00
parent 7dbb9517af
commit 1a4062dd89
10 changed files with 360 additions and 130 deletions

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "create-album-outline-32@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "create-album-outline-32@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "create-album-outline-32@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -13,6 +13,7 @@ protocol ImagePickerGridControllerDelegate: AnyObject {
func imagePicker(_ imagePicker: ImagePickerGridController, didSelectAsset asset: PHAsset, attachmentPromise: Promise<SignalAttachment>)
func imagePicker(_ imagePicker: ImagePickerGridController, didDeselectAsset asset: PHAsset)
var isInBatchSelectMode: Bool { get }
func imagePickerCanSelectAdditionalItems(_ imagePicker: ImagePickerGridController) -> Bool
}
@ -76,8 +77,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
navigationItem.titleView = titleView
self.titleView = titleView
updateSelectButton()
collectionView.backgroundColor = .ows_gray95
let selectionPanGesture = DirectionalPanGestureRecognizer(direction: [.horizontal], target: self, action: #selector(didPanSelection))
@ -94,10 +93,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
@objc
func didPanSelection(_ selectionPanGesture: UIPanGestureRecognizer) {
guard isInBatchSelectMode else {
return
}
guard let collectionView = collectionView else {
owsFailDebug("collectionView was unexpectedly nil")
return
@ -108,6 +103,10 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
return
}
guard delegate.isInBatchSelectMode else {
return
}
switch selectionPanGesture.state {
case .possible:
break
@ -138,11 +137,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
}
func tryToToggleBatchSelect(at indexPath: IndexPath) {
guard isInBatchSelectMode else {
owsFailDebug("isInBatchSelectMode was unexpectedly false")
return
}
guard let collectionView = collectionView else {
owsFailDebug("collectionView was unexpectedly nil")
return
@ -153,6 +147,11 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
return
}
guard delegate.isInBatchSelectMode else {
owsFailDebug("isInBatchSelectMode was unexpectedly false")
return
}
let asset = photoCollectionContents.asset(at: indexPath.item)
switch selectionPanGestureMode {
case .select:
@ -168,8 +167,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
delegate.imagePicker(self, didDeselectAsset: asset)
collectionView.deselectItem(at: indexPath, animated: true)
}
updateDoneButton()
}
override func viewWillLayoutSubviews() {
@ -181,12 +178,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let navBar = self.navigationController?.navigationBar as? OWSNavigationBar {
navBar.overrideTheme(type: .alwaysDark)
} else {
owsFailDebug("Invalid nav bar.")
}
// Determine the size of the thumbnails to request
let scale = UIScreen.main.scale
let cellSize = collectionViewFlowLayout.itemSize
@ -217,10 +208,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
super.viewDidAppear(animated)
hasEverAppeared = true
// done button may have been disable from the last time we hit "Done"
// make sure to re-enable it if appropriate upon returning to the view
hasPressedDoneSinceAppeared = false
updateDoneButton()
// Since we're presenting *over* the ConversationVC, we need to `becomeFirstResponder`.
//
@ -332,78 +319,18 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
// MARK: - Batch Selection
lazy var doneButton: UIBarButtonItem = {
return UIBarButtonItem(barButtonSystemItem: .done,
target: self,
action: #selector(didPressDone))
}()
lazy var selectButton: UIBarButtonItem = {
return UIBarButtonItem(title: NSLocalizedString("BUTTON_SELECT", comment: "Button text to enable batch selection mode"),
style: .plain,
target: self,
action: #selector(didTapSelect))
}()
var isInBatchSelectMode = false {
didSet {
collectionView!.allowsMultipleSelection = isInBatchSelectMode
updateSelectButton()
updateDoneButton()
}
}
@objc
func didPressDone(_ sender: Any) {
Logger.debug("")
func batchSelectModeDidChange() {
guard let delegate = delegate else {
owsFailDebug("delegate was unexpectedly nil")
return
}
hasPressedDoneSinceAppeared = true
updateDoneButton()
delegate.imagePickerDidCompleteSelection(self)
}
var hasPressedDoneSinceAppeared: Bool = false
func updateDoneButton() {
guard let collectionView = self.collectionView else {
guard let collectionView = collectionView else {
owsFailDebug("collectionView was unexpectedly nil")
return
}
guard !hasPressedDoneSinceAppeared else {
doneButton.isEnabled = false
return
}
if let count = collectionView.indexPathsForSelectedItems?.count, count > 0 {
doneButton.isEnabled = true
} else {
doneButton.isEnabled = false
}
}
func updateSelectButton() {
guard !isShowingCollectionPickerController else {
navigationItem.rightBarButtonItem = nil
return
}
let button = isInBatchSelectMode ? doneButton : selectButton
button.tintColor = .ows_gray05
navigationItem.rightBarButtonItem = button
}
@objc
func didTapSelect(_ sender: Any) {
isInBatchSelectMode = true
// disabled until at least one item is selected
self.doneButton.isEnabled = false
collectionView.allowsMultipleSelection = delegate.isInBatchSelectMode
collectionView.reloadData()
}
func clearCollectionViewSelection() {
@ -477,9 +404,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
UIView.animate(.promise, duration: 0.25, delay: 0, options: .curveEaseInOut) {
collectionPickerView.superview?.layoutIfNeeded()
self.updateSelectButton()
self.titleView.rotateIcon(.up)
}.retainUntilComplete()
}
@ -494,9 +418,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
UIView.animate(.promise, duration: 0.25, delay: 0, options: .curveEaseInOut) {
collectionPickerController.view.frame = self.view.frame.offsetBy(dx: 0, dy: self.view.frame.height)
self.updateSelectButton()
self.titleView.rotateIcon(.down)
}.done { _ in
collectionPickerController.view.removeFromSuperview()
@ -550,9 +471,7 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
let attachmentPromise: Promise<SignalAttachment> = photoCollectionContents.outgoingAttachment(for: asset)
delegate.imagePicker(self, didSelectAsset: asset, attachmentPromise: attachmentPromise)
if isInBatchSelectMode {
updateDoneButton()
} else {
if !delegate.isInBatchSelectMode {
// Don't show "selected" badge unless we're in batch mode
collectionView.deselectItem(at: indexPath, animated: false)
delegate.imagePickerDidCompleteSelection(self)
@ -568,10 +487,6 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
let asset = photoCollectionContents.asset(at: indexPath.item)
delegate.imagePicker(self, didDeselectAsset: asset)
if isInBatchSelectMode {
updateDoneButton()
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

View File

@ -19,6 +19,32 @@ class SendMediaNavigationController: OWSNavigationController {
override var prefersStatusBarHidden: Bool { return true }
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
view.addSubview(batchModeButton)
batchModeButton.setCompressionResistanceHigh()
batchModeButton.autoPinEdge(toSuperviewMargin: .bottom)
batchModeButton.autoPinEdge(toSuperviewMargin: .trailing)
view.addSubview(doneButton)
doneButton.setCompressionResistanceHigh()
doneButton.autoPinEdge(toSuperviewMargin: .bottom)
doneButton.autoPinEdge(toSuperviewMargin: .trailing)
view.addSubview(cameraModeButton)
cameraModeButton.setCompressionResistanceHigh()
cameraModeButton.autoPinEdge(toSuperviewMargin: .bottom)
cameraModeButton.autoPinEdge(toSuperviewMargin: .leading)
view.addSubview(mediaLibraryModeButton)
mediaLibraryModeButton.setCompressionResistanceHigh()
mediaLibraryModeButton.autoPinEdge(toSuperviewMargin: .bottom)
mediaLibraryModeButton.autoPinEdge(toSuperviewMargin: .leading)
}
// MARK: -
@objc
@ -27,13 +53,8 @@ class SendMediaNavigationController: OWSNavigationController {
@objc
public class func showingCameraFirst() -> SendMediaNavigationController {
let navController = SendMediaNavigationController()
if let owsNavBar = navController.navigationBar as? OWSNavigationBar {
owsNavBar.overrideTheme(type: .clear)
} else {
owsFailDebug("unexpected navbar: \(navController.navigationBar)")
}
navController.setViewControllers([navController.captureViewController], animated: false)
navController.updateButtons()
return navController
}
@ -41,18 +62,126 @@ class SendMediaNavigationController: OWSNavigationController {
@objc
public class func showingMediaLibraryFirst() -> SendMediaNavigationController {
let navController = SendMediaNavigationController()
if let owsNavBar = navController.navigationBar as? OWSNavigationBar {
owsNavBar.overrideTheme(type: .clear)
} else {
owsFailDebug("unexpected navbar: \(navController.navigationBar)")
}
navController.setViewControllers([navController.mediaLibraryViewController], animated: false)
navController.updateButtons()
return navController
}
// MARK:
var isInBatchSelectMode = false {
didSet {
if oldValue != isInBatchSelectMode {
updateButtons()
mediaLibraryViewController.batchSelectModeDidChange()
}
}
}
func updateButtons() {
guard let topViewController = viewControllers.last else {
return
}
switch topViewController {
case is AttachmentApprovalViewController:
batchModeButton.isHidden = true
doneButton.isHidden = true
cameraModeButton.isHidden = true
mediaLibraryModeButton.isHidden = true
case is ImagePickerGridController:
batchModeButton.isHidden = isInBatchSelectMode
doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
cameraModeButton.isHidden = false
mediaLibraryModeButton.isHidden = true
case is PhotoCaptureViewController:
batchModeButton.isHidden = isInBatchSelectMode
doneButton.isHidden = !isInBatchSelectMode || (attachmentDraftCollection.count == 0 && mediaLibrarySelections.count == 0)
cameraModeButton.isHidden = true
mediaLibraryModeButton.isHidden = false
default:
owsFailDebug("unexpected topViewController: \(topViewController)")
}
doneButton.updateCount()
}
func fadeTo(viewControllers: [UIViewController]) {
let transition: CATransition = CATransition()
transition.duration = 0.1
transition.type = kCATransitionFade
view.layer.add(transition, forKey: nil)
setViewControllers(viewControllers, animated: false)
}
// MARK: - Events
private func didTapBatchModeButton() {
// There's no way to _disable_ batch mode.
isInBatchSelectMode = true
}
private func didTapCameraModeButton() {
fadeTo(viewControllers: [captureViewController])
updateButtons()
}
private func didTapMediaLibraryModeButton() {
fadeTo(viewControllers: [mediaLibraryViewController])
updateButtons()
}
// MARK: Views
private lazy var doneButton: DoneButton = {
let button = DoneButton()
button.delegate = self
return button
}()
private lazy var batchModeButton: UIButton = {
let button = OWSButton(imageName: "media_send_batch_mode_disabled",
tintColor: .ows_gray60,
block: { [weak self] in self?.didTapBatchModeButton() })
let width: CGFloat = 44
button.autoSetDimensions(to: CGSize(width: width, height: width))
button.layer.cornerRadius = width / 2
button.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
button.backgroundColor = .ows_white
return button
}()
private lazy var cameraModeButton: UIButton = {
let button = OWSButton(imageName: "settings-avatar-camera-2",
tintColor: .ows_gray60,
block: { [weak self] in self?.didTapCameraModeButton() })
let width: CGFloat = 44
button.autoSetDimensions(to: CGSize(width: width, height: width))
button.layer.cornerRadius = width / 2
button.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
button.backgroundColor = .ows_white
return button
}()
private lazy var mediaLibraryModeButton: UIButton = {
let button = OWSButton(imageName: "actionsheet_camera_roll_black",
tintColor: .ows_gray60,
block: { [weak self] in self?.didTapMediaLibraryModeButton() })
let width: CGFloat = 44
button.autoSetDimensions(to: CGSize(width: width, height: width))
button.layer.cornerRadius = width / 2
button.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
button.backgroundColor = .ows_white
return button
}()
// MARK: State
private var attachmentDraftCollection: AttachmentDraftCollection = .empty
@ -83,22 +212,60 @@ class SendMediaNavigationController: OWSNavigationController {
approvalViewController.approvalDelegate = self
pushViewController(approvalViewController, animated: true)
updateButtons()
}
}
extension SendMediaNavigationController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if let navbarTheme = preferredNavbarTheme(viewController: viewController) {
if let owsNavBar = navigationBar as? OWSNavigationBar {
owsNavBar.overrideTheme(type: navbarTheme)
} else {
owsFailDebug("unexpected navigationBar: \(navigationBar)")
}
}
}
// In case back navigation was canceled, we re-apply whatever is showing.
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if let navbarTheme = preferredNavbarTheme(viewController: viewController) {
if let owsNavBar = navigationBar as? OWSNavigationBar {
owsNavBar.overrideTheme(type: navbarTheme)
} else {
owsFailDebug("unexpected navigationBar: \(navigationBar)")
}
}
}
// MARK: - Helpers
private func preferredNavbarTheme(viewController: UIViewController) -> OWSNavigationBar.NavigationBarThemeOverride? {
switch viewController {
case is AttachmentApprovalViewController:
return .clear
case is ImagePickerGridController:
return .alwaysDark
case is PhotoCaptureViewController:
return .clear
default:
owsFailDebug("unexpected viewController: \(viewController)")
return nil
}
}
}
extension SendMediaNavigationController: PhotoCaptureViewControllerDelegate {
func photoCaptureViewController(_ photoCaptureViewController: PhotoCaptureViewController, didFinishProcessingAttachment attachment: SignalAttachment) {
attachmentDraftCollection.append(.camera(attachment: attachment))
pushApprovalViewController()
if isInBatchSelectMode {
updateButtons()
} else {
pushApprovalViewController()
}
}
func photoCaptureViewControllerDidCancel(_ photoCaptureViewController: PhotoCaptureViewController) {
// TODO
// sometimes we might want this to be a "back" to the approval view
// other times we might want this to be a "close" and take me back to the CVC
// seems like we should show the "back" and have a seprate "didTapBack" delegate method or something...
self.sendMediaNavDelegate?.sendMediaNavDidCancel(self)
}
}
@ -106,6 +273,10 @@ extension SendMediaNavigationController: PhotoCaptureViewControllerDelegate {
extension SendMediaNavigationController: ImagePickerGridControllerDelegate {
func imagePickerDidCompleteSelection(_ imagePicker: ImagePickerGridController) {
showApprovalAfterProcessingAnyMediaLibrarySelections()
}
func showApprovalAfterProcessingAnyMediaLibrarySelections() {
let mediaLibrarySelections: [MediaLibrarySelection] = self.mediaLibrarySelections.orderedValues
let backgroundBlock: (ModalActivityIndicatorViewController) -> Void = { modal in
@ -141,11 +312,15 @@ extension SendMediaNavigationController: ImagePickerGridControllerDelegate {
let libraryMedia = MediaLibrarySelection(asset: asset, signalAttachmentPromise: attachmentPromise)
mediaLibrarySelections.append(key: asset, value: libraryMedia)
updateButtons()
}
func imagePicker(_ imagePicker: ImagePickerGridController, didDeselectAsset asset: PHAsset) {
if mediaLibrarySelections.hasValue(forKey: asset) {
mediaLibrarySelections.remove(key: asset)
updateButtons()
}
}
@ -184,19 +359,21 @@ extension SendMediaNavigationController: AttachmentApprovalViewControllerDelegat
assert(viewControllers.count == 2)
// regardless of which VC we're going "back" to, we're in "batch" mode at this point.
mediaLibraryViewController.isInBatchSelectMode = true
mediaLibraryViewController.collectionView?.reloadData()
isInBatchSelectMode = true
mediaLibraryViewController.batchSelectModeDidChange()
popViewController(animated: true)
popViewController(animated: true) {
self.updateButtons()
}
}
}
enum AttachmentDraft {
private enum AttachmentDraft {
case camera(attachment: SignalAttachment)
case picker(attachment: MediaLibraryAttachment)
}
extension AttachmentDraft {
private extension AttachmentDraft {
var attachment: SignalAttachment {
switch self {
case .camera(let cameraAttachment):
@ -211,7 +388,7 @@ extension AttachmentDraft {
}
}
struct AttachmentDraftCollection {
private struct AttachmentDraftCollection {
private(set) var attachmentDrafts: [AttachmentDraft]
static var empty: AttachmentDraftCollection {
@ -261,7 +438,7 @@ struct AttachmentDraftCollection {
}
}
struct MediaLibrarySelection: Hashable, Equatable {
private struct MediaLibrarySelection: Hashable, Equatable {
let asset: PHAsset
let signalAttachmentPromise: Promise<SignalAttachment>
@ -281,7 +458,7 @@ struct MediaLibrarySelection: Hashable, Equatable {
}
}
struct MediaLibraryAttachment: Hashable, Equatable {
private struct MediaLibraryAttachment: Hashable, Equatable {
let asset: PHAsset
let signalAttachment: SignalAttachment
@ -293,3 +470,105 @@ struct MediaLibraryAttachment: Hashable, Equatable {
return lhs.asset == rhs.asset
}
}
extension SendMediaNavigationController: DoneButtonDelegate {
var doneButtonCount: Int {
return attachmentDraftCollection.count - attachmentDraftCollection.pickerAttachments.count + mediaLibrarySelections.count
}
fileprivate func doneButtonWasTapped(_ doneButton: DoneButton) {
assert(attachmentDraftCollection.count > 0 || mediaLibrarySelections.count > 0)
showApprovalAfterProcessingAnyMediaLibrarySelections()
}
}
private protocol DoneButtonDelegate: AnyObject {
func doneButtonWasTapped(_ doneButton: DoneButton)
var doneButtonCount: Int { get }
}
private class DoneButton: UIView {
weak var delegate: DoneButtonDelegate?
init() {
super.init(frame: .zero)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(tapGesture:)))
addGestureRecognizer(tapGesture)
let container = UIView()
container.backgroundColor = .ows_white
container.layer.cornerRadius = 20
container.layoutMargins = UIEdgeInsets(top: 7, leading: 8, bottom: 7, trailing: 8)
addSubview(container)
container.autoPinEdgesToSuperviewMargins()
let stackView = UIStackView(arrangedSubviews: [badge, chevron])
stackView.axis = .horizontal
stackView.alignment = .center
stackView.spacing = 9
container.addSubview(stackView)
stackView.autoPinEdgesToSuperviewMargins()
}
let numberFormatter: NumberFormatter = NumberFormatter()
func updateCount() {
guard let delegate = delegate else {
return
}
badgeLabel.text = numberFormatter.string(for: delegate.doneButtonCount)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Subviews
private lazy var badge: UIView = {
let badge = CircleView()
badge.layoutMargins = UIEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
badge.backgroundColor = .ows_signalBlue
badge.addSubview(badgeLabel)
badgeLabel.autoPinEdgesToSuperviewMargins()
// Constrain to be a pill that is at least a circle, and maybe wider.
badgeLabel.autoPin(toAspectRatio: 1.0, relation: .greaterThanOrEqual)
NSLayoutConstraint.autoSetPriority(.defaultLow) {
badgeLabel.autoPinToSquareAspectRatio()
}
return badge
}()
private lazy var badgeLabel: UILabel = {
let label = UILabel()
label.textColor = .ows_white
label.font = UIFont.ows_dynamicTypeSubheadline.ows_monospaced()
label.textAlignment = .center
return label
}()
private lazy var chevron: UIView = {
let image: UIImage
if CurrentAppContext().isRTL {
image = #imageLiteral(resourceName: "small_chevron_left")
} else {
image = #imageLiteral(resourceName: "small_chevron_right")
}
let chevron = UIImageView(image: image.withRenderingMode(.alwaysTemplate))
chevron.contentMode = .scaleAspectFit
chevron.tintColor = .ows_gray60
chevron.autoSetDimensions(to: CGSize(width: 10, height: 18))
return chevron
}()
@objc
func didTap(tapGesture: UITapGestureRecognizer) {
delegate?.doneButtonWasTapped(self)
}
}

View File

@ -56,6 +56,7 @@ NS_ASSUME_NONNULL_BEGIN
- (UIFont *)ows_italic;
- (UIFont *)ows_bold;
- (UIFont *)ows_mediumWeight;
- (UIFont *)ows_monospaced;
@end

View File

@ -229,6 +229,12 @@ NS_ASSUME_NONNULL_BEGIN
return derivedFont;
}
- (UIFont *)ows_monospaced
{
return [self.class ows_monospacedDigitFontWithSize:self.pointSize];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -43,6 +43,7 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value);
- (NSLayoutConstraint *)autoPinToSquareAspectRatio;
- (NSLayoutConstraint *)autoPinToAspectRatioWithSize:(CGSize)size;
- (NSLayoutConstraint *)autoPinToAspectRatio:(CGFloat)ratio;
- (NSLayoutConstraint *)autoPinToAspectRatio:(CGFloat)ratio relation:(NSLayoutRelation)relation;
#pragma mark - Content Hugging and Compression Resistance

View File

@ -2,8 +2,8 @@
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "OWSMath.h"
#import "UIView+OWS.h"
#import "OWSMath.h"
#import <SignalCoreKit/iOSVersions.h>
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/AppContext.h>
@ -148,6 +148,11 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
}
- (NSLayoutConstraint *)autoPinToAspectRatio:(CGFloat)ratio
{
return [self autoPinToAspectRatio:ratio relation:NSLayoutRelationEqual];
}
- (NSLayoutConstraint *)autoPinToAspectRatio:(CGFloat)ratio relation:(NSLayoutRelation)relation
{
// Clamp to ensure view has reasonable aspect ratio.
CGFloat clampedRatio = CGFloatClamp(ratio, 0.05f, 95.0f);
@ -158,7 +163,7 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
self.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
relatedBy:relation
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:clampedRatio