Signal-iOS/SignalUI/Views/ImageEditor/ImageEditorToolbar.swift
Igor Solomennikov 6548e374e2
Improved crop tool in media editor.
Re-do all crop view controller with pure Auto Layout which allowed to animate layout changes and transitions between UI states.

Image rotation using two fingers was removed to match native Photos app: tilt control UI at the bottom allows much more precise rotation and two finger gesture is now purely for zooming and panning.
2022-07-26 14:11:03 -07:00

135 lines
5.2 KiB
Swift

//
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import UIKit
class ImageEditorTopBar: MediaTopBar {
let undoButton = RoundMediaButton(image: #imageLiteral(resourceName: "media-editor-undo"), backgroundStyle: .blur)
var isUndoButtonHidden: Bool {
get { undoButton.alpha == 0 }
set { undoButton.alpha = newValue ? 0 : 1 }
}
let clearAllButton = RoundMediaButton(image: nil, backgroundStyle: .blur)
var isClearAllButtonHidden: Bool {
get { clearAllButton.alpha == 0 }
set { clearAllButton.alpha = newValue ? 0 : 1 }
}
override init(frame: CGRect) {
super.init(frame: frame)
let clearAllButtonTitle =
OWSLocalizedString("MEDIA_EDITOR_CLEAR_ALL",
comment: "Title for the button that discards all edits in media editor.")
clearAllButton.setTitle(clearAllButtonTitle, for: .normal)
clearAllButton.contentEdgeInsets = UIEdgeInsets(hMargin: 18, vMargin: 7)
clearAllButton.layoutMargins = .zero
let stackView = UIStackView(arrangedSubviews: [ undoButton, UIView.hStretchingSpacer(), clearAllButton ])
for button in stackView.arrangedSubviews {
button.setContentHuggingPriority(.defaultHigh, for: .vertical)
button.setCompressionResistanceVerticalHigh()
}
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.alignment = .center
stackView.isOpaque = false
addSubview(stackView)
addConstraints([
undoButton.layoutMarginsGuide.leadingAnchor.constraint(equalTo: controlsLayoutGuide.leadingAnchor),
stackView.topAnchor.constraint(equalTo: controlsLayoutGuide.topAnchor),
stackView.bottomAnchor.constraint(equalTo: controlsLayoutGuide.bottomAnchor),
stackView.trailingAnchor.constraint(equalTo: controlsLayoutGuide.trailingAnchor)
])
}
@available(*, unavailable, message: "Use init(frame:)")
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
protocol ImageEditorBottomBarButtonProvider: AnyObject {
var middleButtons: [UIButton] { get }
}
protocol ImageEditorBottomBarProvider: AnyObject {
func bottomBar(for viewController: UIViewController) -> ImageEditorBottomBar
}
class ImageEditorBottomBar: UIView {
let cancelButton: UIButton = RoundMediaButton(image: #imageLiteral(resourceName: "media-editor-toolbar-discard"),
backgroundStyle: .solid(RoundMediaButton.defaultBackgroundColor))
let doneButton: UIButton = RoundMediaButton(image: #imageLiteral(resourceName: "media-editor-toolbar-done"),
backgroundStyle: .solid(RoundMediaButton.defaultBackgroundColor))
let buttons: [UIButton]
let stackView = UIStackView()
private var areControlsHidden = false
private var stackViewPositionConstraint: NSLayoutConstraint?
required init(buttonProvider: ImageEditorBottomBarButtonProvider?) {
let middleButtons = buttonProvider?.middleButtons ?? []
self.buttons = [ cancelButton ] + middleButtons + [ doneButton ]
super.init(frame: .zero)
preservesSuperviewLayoutMargins = true
setContentHuggingVerticalHigh()
// Constrain bottom edge to bottom safe area.
if UIDevice.current.hasIPhoneXNotch {
layoutMargins.bottom = 0
}
buttons.forEach { button in
button.setContentHuggingHigh()
button.setCompressionResistanceVerticalHigh()
}
let middleStackView = UIStackView(arrangedSubviews: middleButtons)
middleStackView.spacing = 2
stackView.addArrangedSubviews([ cancelButton, middleStackView, doneButton ])
stackView.distribution = .equalSpacing
stackView.isOpaque = false
addSubview(stackView)
stackView.autoPinLeadingToSuperviewMargin(withInset: -cancelButton.layoutMargins.leading)
stackView.autoPinTrailingToSuperviewMargin(withInset: -doneButton.layoutMargins.trailing)
stackView.heightAnchor.constraint(equalTo: layoutMarginsGuide.heightAnchor).isActive = true
setControls(hidden: false)
}
@available(*, unavailable, message: "Use init(buttonProvider:)")
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setControls(hidden: Bool) {
guard hidden != areControlsHidden || stackViewPositionConstraint == nil else { return }
if let stackViewPositionConstraint = stackViewPositionConstraint {
removeConstraint(stackViewPositionConstraint)
self.stackViewPositionConstraint = nil
}
let stackViewPositionConstraint: NSLayoutConstraint
if hidden {
stackViewPositionConstraint = stackView.topAnchor.constraint(equalTo: bottomAnchor)
} else {
stackViewPositionConstraint = stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
}
addConstraint(stackViewPositionConstraint)
self.stackViewPositionConstraint = stackViewPositionConstraint
areControlsHidden = hidden
}
}