Merge branch 'charlesmchen/imageEditorDesign1'

pull/2/head
Matthew Chen 6 years ago
commit 6095500f80

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -2,15 +2,17 @@
"images" : [
{
"idiom" : "universal",
"filename" : "Screen Shot 2019-02-26 at 1.57.23 PM.png",
"filename" : "crop-32@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "crop-32@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "crop-32@3x.png",
"scale" : "3x"
}
],

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -953,7 +953,8 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
autoPinView(toBottomOfViewControllerOrKeyboard: imageEditorView, avoidNotch: true)
imageEditorView.autoPinWidthToSuperview()
imageEditorView.addControls(to: imageEditorView)
imageEditorView.addControls(to: imageEditorView,
viewController: self)
}
}
#endif

@ -78,32 +78,40 @@ class ImageEditorCropViewController: OWSViewController {
// MARK: - View Lifecycle
private var isCropLocked = false
private var cropLockButton: OWSButton?
override func loadView() {
self.view = UIView()
self.view.backgroundColor = .black
self.view.layoutMargins = .zero
// MARK: - Buttons
// TODO: Apply icons.
let doneButton = OWSButton(title: "Done") { [weak self] in
let doneButton = OWSButton(imageName: "image_editor_checkmark_full",
tintColor: UIColor.white) { [weak self] in
self?.didTapBackButton()
}
let rotate90Button = OWSButton(title: "Rotate 90°") { [weak self] in
let rotate90Button = OWSButton(imageName: "image_editor_rotate",
tintColor: UIColor.white) { [weak self] in
self?.rotate90ButtonPressed()
}
let rotate45Button = OWSButton(title: "Rotate 45°") { [weak self] in
self?.rotate45ButtonPressed()
}
let resetButton = OWSButton(title: "Reset") { [weak self] in
// TODO: Myles may change this asset.
let resetButton = OWSButton(imageName: "image_editor_undo",
tintColor: UIColor.white) { [weak self] in
self?.resetButtonPressed()
}
let zoom2xButton = OWSButton(title: "Zoom 2x") { [weak self] in
self?.zoom2xButtonPressed()
let flipButton = OWSButton(imageName: "image_editor_flip",
tintColor: UIColor.white) { [weak self] in
self?.flipButtonPressed()
}
let flipButton = OWSButton(title: "Flip") { [weak self] in
self?.flipButtonPressed()
let cropLockButton = OWSButton(imageName: "image_editor_crop_unlock",
tintColor: UIColor.white) { [weak self] in
self?.cropLockButtonPressed()
}
self.cropLockButton = cropLockButton
// MARK: - Header
@ -152,17 +160,17 @@ class ImageEditorCropViewController: OWSViewController {
// MARK: - Footer
let footer = UIStackView(arrangedSubviews: [
flipButton,
rotate90Button,
rotate45Button,
flipButton,
UIView.hStretchingSpacer(),
zoom2xButton
cropLockButton
])
footer.axis = .horizontal
footer.spacing = 16
footer.backgroundColor = .clear
footer.isOpaque = false
let imageMargin: CGFloat = 20
let stackView = UIStackView(arrangedSubviews: [
header,
wrapperView,
@ -170,8 +178,8 @@ class ImageEditorCropViewController: OWSViewController {
])
stackView.axis = .vertical
stackView.alignment = .fill
stackView.spacing = 24
stackView.layoutMargins = UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20)
stackView.spacing = imageMargin
stackView.layoutMargins = UIEdgeInsets(top: 8, left: imageMargin, bottom: 8, right: imageMargin)
stackView.isLayoutMarginsRelativeArrangement = true
self.view.addSubview(stackView)
stackView.autoPinEdgesToSuperviewEdges()
@ -211,6 +219,16 @@ class ImageEditorCropViewController: OWSViewController {
configureGestures()
}
private func updateCropLockButton() {
guard let cropLockButton = cropLockButton else {
owsFailDebug("Missing cropLockButton")
return
}
cropLockButton.setImage(imageName: (isCropLocked
? "image_editor_crop_lock"
: "image_editor_crop_unlock"))
}
private static let desiredCornerSize: CGFloat = 24
private static let minCropSize: CGFloat = desiredCornerSize * 2
private var cornerSize = CGSize.zero
@ -500,6 +518,14 @@ class ImageEditorCropViewController: OWSViewController {
let cropRectangleStart = clipView.bounds
var cropRectangleNow = cropRectangleStart
// Derive the new crop rectangle.
// We limit the crop rectangle's minimum size for two reasons.
//
// * To ensure that the crop rectangles "corner handles"
// can always be safely drawn.
// * To avoid awkward interactions when the crop rectangle
// is very small. Users can always crop multiple times.
let maxDeltaX = cropRectangleNow.size.width - cornerSize.width * 2
let maxDeltaY = cropRectangleNow.size.height - cornerSize.height * 2
@ -527,6 +553,44 @@ class ImageEditorCropViewController: OWSViewController {
break
}
// If crop is locked, update the crop rectangle
// to retain the original aspect ratio.
if (isCropLocked) {
let scaleX = cropRectangleNow.width / cropRectangleStart.width
let scaleY = cropRectangleNow.height / cropRectangleStart.height
var cropRectangleLocked = cropRectangleStart
// Find a new crop rectangle size with the correct aspect
// ratio which is always larger than the "naive" crop rectangle.
// We always expand and never shrink the crop rectangle to
// fix its aspect ratio, to ensure the "max deltas" enforced
// above still are honored.
if scaleX > scaleY {
cropRectangleLocked.size.width = cropRectangleNow.width
cropRectangleLocked.size.height = cropRectangleNow.width * cropRectangleStart.height / cropRectangleStart.width
} else {
cropRectangleLocked.size.height = cropRectangleNow.height
cropRectangleLocked.size.width = cropRectangleNow.height * cropRectangleStart.width / cropRectangleStart.height
}
// Pin the crop rectangle to the sides that aren't being manipulated.
switch panCropRegion {
case .left, .topLeft, .bottomLeft:
cropRectangleLocked.origin.x = cropRectangleStart.maxX - cropRectangleLocked.width
default:
// Bias towards aligning left.
cropRectangleLocked.origin.x = cropRectangleStart.minX
}
switch panCropRegion {
case .top, .topLeft, .topRight:
cropRectangleLocked.origin.y = cropRectangleStart.maxY - cropRectangleLocked.height
default:
// Bias towards aligning top.
cropRectangleLocked.origin.y = cropRectangleStart.minY
}
cropRectangleNow = cropRectangleLocked
}
cropView.frame = view.convert(cropRectangleNow, from: clipView)
switch gestureRecognizer.state {
@ -683,10 +747,6 @@ class ImageEditorCropViewController: OWSViewController {
rotateButtonPressed(angleRadians: CGFloat.pi * 0.5, rotateCanvas: true)
}
@objc public func rotate45ButtonPressed() {
rotateButtonPressed(angleRadians: CGFloat.pi * 0.25, rotateCanvas: false)
}
private func rotateButtonPressed(angleRadians: CGFloat, rotateCanvas: Bool) {
let outputSizePixels = (rotateCanvas
// Invert width and height.
@ -703,18 +763,6 @@ class ImageEditorCropViewController: OWSViewController {
isFlipped: transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels))
}
@objc public func zoom2xButtonPressed() {
let outputSizePixels = transform.outputSizePixels
let unitTranslation = transform.unitTranslation
let rotationRadians = transform.rotationRadians
let scaling = transform.scaling * 2.0
updateTransform(ImageEditorTransform(outputSizePixels: outputSizePixels,
unitTranslation: unitTranslation,
rotationRadians: rotationRadians,
scaling: scaling,
isFlipped: transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels))
}
@objc public func flipButtonPressed() {
updateTransform(ImageEditorTransform(outputSizePixels: transform.outputSizePixels,
unitTranslation: transform.unitTranslation,
@ -726,6 +774,11 @@ class ImageEditorCropViewController: OWSViewController {
@objc public func resetButtonPressed() {
updateTransform(ImageEditorTransform.defaultTransform(srcImageSizePixels: model.srcImageSizePixels))
}
@objc public func cropLockButtonPressed() {
isCropLocked = !isCropLocked
updateCropLockButton()
}
}
// MARK: -

@ -39,8 +39,10 @@ public class ImageEditorPaletteView: UIView {
self.backgroundColor = .clear
self.isOpaque = false
if let image = UIImage(named: "image_editor_palette") {
if let image = ImageEditorPaletteView.buildPaletteGradientImage() {
imageView.image = image
imageView.layer.cornerRadius = image.size.width * 0.5
imageView.clipsToBounds = true
} else {
owsFailDebug("Missing image.")
}
@ -48,8 +50,6 @@ public class ImageEditorPaletteView: UIView {
// We use an invisible margin to expand the hot area of
// this control.
let margin: CGFloat = 8
// TODO: Review sizing when there's an asset.
imageView.autoSetDimensions(to: CGSize(width: 8, height: 200))
imageView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin))
selectionWrapper.layoutCallback = { [weak self] (view) in
@ -58,8 +58,8 @@ public class ImageEditorPaletteView: UIView {
}
strongSelf.updateState()
}
imageView.addSubview(selectionWrapper)
selectionWrapper.autoPinEdgesToSuperviewEdges()
self.addSubview(selectionWrapper)
selectionWrapper.autoPin(toEdgesOf: imageView)
selectionView.addBorder(with: .white)
selectionView.layer.cornerRadius = selectionSize / 2
@ -95,9 +95,8 @@ public class ImageEditorPaletteView: UIView {
private func updateState() {
var selectedColor = UIColor.white
if let image = imageView.image,
let cgImage = image.cgImage {
if let imageColor = image.color(atLocation: CGPoint(x: CGFloat(cgImage.width) * 0.5, y: CGFloat(cgImage.height) * selectionAlpha)) {
if let image = imageView.image {
if let imageColor = image.color(atLocation: CGPoint(x: CGFloat(image.size.width) * 0.5, y: CGFloat(image.size.height) * selectionAlpha)) {
selectedColor = imageColor
} else {
owsFailDebug("Couldn't determine image color.")
@ -132,6 +131,32 @@ public class ImageEditorPaletteView: UIView {
let location = gesture.location(in: imageView)
selectColor(atLocationY: location.y)
}
private static func buildPaletteGradientImage() -> UIImage? {
let gradientSize = CGSize(width: 8, height: 200)
let gradientBounds = CGRect(origin: .zero, size: gradientSize)
let gradientView = UIView()
gradientView.frame = gradientBounds
let gradientLayer = CAGradientLayer()
gradientView.layer.addSublayer(gradientLayer)
gradientLayer.frame = gradientBounds
// See: https://github.com/signalapp/Signal-Android/blob/master/res/values/arrays.xml#L267
gradientLayer.colors = [
UIColor(rgbHex: 0xffffff).cgColor,
UIColor(rgbHex: 0xff0000).cgColor,
UIColor(rgbHex: 0xff00ff).cgColor,
UIColor(rgbHex: 0x0000ff).cgColor,
UIColor(rgbHex: 0x00ffff).cgColor,
UIColor(rgbHex: 0x00ff00).cgColor,
UIColor(rgbHex: 0xffff00).cgColor,
UIColor(rgbHex: 0xff5500).cgColor,
UIColor(rgbHex: 0x000000).cgColor
]
gradientLayer.startPoint = CGPoint.zero
gradientLayer.endPoint = CGPoint(x: 0, y: gradientSize.height)
gradientLayer.endPoint = CGPoint(x: 0, y: 1.0)
return gradientView.renderAsImage(opaque: true, scale: UIScreen.main.scale)
}
}
// MARK: -
@ -162,6 +187,9 @@ extension UIImage {
owsFailDebug("Invalid image size.")
return nil
}
Logger.verbose("scale: \(self.scale)")
// Convert the location from points to pixels and clamp to the image bounds.
let xPixels: Int = Int(round(locationPoints.x * self.scale)).clamp(0, imageWidth - 1)
let yPixels: Int = Int(round(locationPoints.y * self.scale)).clamp(0, imageHeight - 1)

@ -29,12 +29,14 @@ public class ImageEditorView: UIView {
// This is the default mode. It is used for interacting with text items.
case none
case brush
case text
}
private var editorMode = EditorMode.none {
didSet {
AssertIsOnMainThread()
updateButtons()
updateGestureState()
}
}
@ -129,46 +131,50 @@ public class ImageEditorView: UIView {
}
}
private let undoButton = UIButton(type: .custom)
private let redoButton = UIButton(type: .custom)
private let brushButton = UIButton(type: .custom)
private let cropButton = UIButton(type: .custom)
private let newTextButton = UIButton(type: .custom)
private var allButtons = [UIButton]()
// The model supports redo if we ever want to add it.
private let undoButton = OWSButton()
private let brushButton = OWSButton()
private let cropButton = OWSButton()
private let newTextButton = OWSButton()
private let captionButton = OWSButton()
private let doneButton = OWSButton()
private let buttonStackView = UIStackView()
// TODO: Should this method be private?
@objc
public func addControls(to containerView: UIView) {
public func addControls(to containerView: UIView,
viewController: UIViewController) {
configure(button: undoButton,
label: NSLocalizedString("BUTTON_UNDO", comment: "Label for undo button."),
imageName: "image_editor_undo",
selector: #selector(didTapUndo(sender:)))
configure(button: redoButton,
label: NSLocalizedString("BUTTON_REDO", comment: "Label for redo button."),
selector: #selector(didTapRedo(sender:)))
configure(button: brushButton,
label: NSLocalizedString("IMAGE_EDITOR_BRUSH_BUTTON", comment: "Label for brush button in image editor."),
imageName: "image_editor_brush",
selector: #selector(didTapBrush(sender:)))
configure(button: cropButton,
label: NSLocalizedString("IMAGE_EDITOR_CROP_BUTTON", comment: "Label for crop button in image editor."),
imageName: "image_editor_crop",
selector: #selector(didTapCrop(sender:)))
configure(button: newTextButton,
label: "Text",
imageName: "image_editor_text",
selector: #selector(didTapNewText(sender:)))
allButtons = [brushButton, cropButton, undoButton, redoButton, newTextButton]
configure(button: captionButton,
imageName: "image_editor_caption",
selector: #selector(didTapCaption(sender:)))
configure(button: doneButton,
imageName: "image_editor_checkmark_full",
selector: #selector(didTapDone(sender:)))
let stackView = UIStackView(arrangedSubviews: allButtons)
stackView.axis = .vertical
stackView.alignment = .center
stackView.spacing = 10
buttonStackView.axis = .horizontal
buttonStackView.alignment = .center
buttonStackView.spacing = 20
containerView.addSubview(stackView)
stackView.autoAlignAxis(toSuperviewAxis: .horizontal)
stackView.autoPinTrailingToSuperviewMargin(withInset: 10)
containerView.addSubview(buttonStackView)
buttonStackView.autoPin(toTopLayoutGuideOf: viewController, withInset: 0)
buttonStackView.autoPinTrailingToSuperviewMargin(withInset: 18)
containerView.addSubview(paletteView)
paletteView.autoVCenterInSuperview()
@ -178,26 +184,54 @@ public class ImageEditorView: UIView {
}
private func configure(button: UIButton,
label: String,
imageName: String,
selector: Selector) {
button.setTitle(label, for: .normal)
button.setTitleColor(.white, for: .normal)
button.setTitleColor(.gray, for: .disabled)
button.setTitleColor(UIColor.ows_materialBlue, for: .selected)
button.titleLabel?.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight()
if let image = UIImage(named: imageName) {
button.setImage(image.withRenderingMode(.alwaysTemplate), for: .normal)
} else {
owsFailDebug("Missing asset: \(imageName)")
}
button.tintColor = .white
button.addTarget(self, action: selector, for: .touchUpInside)
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowRadius = 4
button.layer.shadowOpacity = 0.66
}
private func updateButtons() {
undoButton.isEnabled = model.canUndo()
redoButton.isEnabled = model.canRedo()
brushButton.isSelected = editorMode == .brush
cropButton.isSelected = false
newTextButton.isSelected = false
for button in allButtons {
button.isHidden = isEditingTextItem
var buttons = [OWSButton]()
var hasPalette = false
switch editorMode {
case .text:
// TODO:
hasPalette = true
break
case .brush:
hasPalette = true
if model.canUndo() {
buttons = [undoButton, doneButton]
} else {
buttons = [doneButton]
}
case .none:
if model.canUndo() {
buttons = [undoButton, newTextButton, brushButton, cropButton, captionButton]
} else {
buttons = [newTextButton, brushButton, cropButton, captionButton]
}
}
for subview in buttonStackView.subviews {
subview.removeFromSuperview()
}
buttonStackView.addArrangedSubview(UIView.hStretchingSpacer())
for button in buttons {
buttonStackView.addArrangedSubview(button)
}
paletteView.isHidden = !hasPalette
}
// MARK: - Actions
@ -211,19 +245,10 @@ public class ImageEditorView: UIView {
model.undo()
}
@objc func didTapRedo(sender: UIButton) {
Logger.verbose("")
guard model.canRedo() else {
owsFailDebug("Can't redo.")
return
}
model.redo()
}
@objc func didTapBrush(sender: UIButton) {
Logger.verbose("")
toggle(editorMode: .brush)
self.editorMode = .brush
}
@objc func didTapCrop(sender: UIButton) {
@ -250,13 +275,16 @@ public class ImageEditorView: UIView {
edit(textItem: textItem)
}
func toggle(editorMode: EditorMode) {
if self.editorMode == editorMode {
self.editorMode = .none
} else {
self.editorMode = editorMode
}
updateButtons()
@objc func didTapCaption(sender: UIButton) {
Logger.verbose("")
// TODO:
}
@objc func didTapDone(sender: UIButton) {
Logger.verbose("")
self.editorMode = .none
}
// MARK: - Gestures
@ -276,6 +304,11 @@ public class ImageEditorView: UIView {
brushGestureRecognizer?.isEnabled = true
tapGestureRecognizer?.isEnabled = false
pinchGestureRecognizer?.isEnabled = false
case .text:
moveTextGestureRecognizer?.isEnabled = false
brushGestureRecognizer?.isEnabled = false
tapGestureRecognizer?.isEnabled = false
pinchGestureRecognizer?.isEnabled = false
}
}
@ -543,20 +576,10 @@ public class ImageEditorView: UIView {
// MARK: - Edit Text Tool
private var isEditingTextItem = false {
didSet {
AssertIsOnMainThread()
updateButtons()
}
}
private func edit(textItem: ImageEditorTextItem) {
Logger.verbose("")
toggle(editorMode: .none)
isEditingTextItem = true
self.editorMode = .text
// TODO:
let maxTextWidthPoints = model.srcImageSizePixels.width * ImageEditorTextItem.kDefaultUnitWidth
@ -572,7 +595,7 @@ public class ImageEditorView: UIView {
private func presentCropTool() {
Logger.verbose("")
toggle(editorMode: .none)
self.editorMode = .none
guard let srcImage = canvasView.loadSrcImage() else {
owsFailDebug("Couldn't load src image.")
@ -634,7 +657,7 @@ extension ImageEditorView: ImageEditorTextViewControllerDelegate {
public func textEditDidComplete(textItem: ImageEditorTextItem, text: String?) {
AssertIsOnMainThread()
isEditingTextItem = false
self.editorMode = .none
guard let text = text?.ows_stripped(),
text.count > 0 else {
@ -654,7 +677,7 @@ extension ImageEditorView: ImageEditorTextViewControllerDelegate {
}
public func textEditDidCancel() {
isEditingTextItem = false
self.editorMode = .none
}
}

@ -29,6 +29,28 @@ public class OWSButton: UIButton {
setTitle(title, for: .normal)
}
@objc
init(imageName: String,
tintColor: UIColor,
block: @escaping () -> Void = { }) {
super.init(frame: .zero)
self.block = block
addTarget(self, action: #selector(didTap), for: .touchUpInside)
setImage(imageName: imageName)
self.tintColor = tintColor
}
@objc
public func setImage(imageName: String) {
if let image = UIImage(named: imageName) {
setImage(image.withRenderingMode(.alwaysTemplate), for: .normal)
} else {
owsFailDebug("Missing asset: \(imageName)")
}
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

Loading…
Cancel
Save