Pinch to change text size in image editor text tool.
This commit is contained in:
parent
b86ff1425e
commit
88c07fc534
|
@ -15,6 +15,7 @@
|
|||
34080F0022282C880087E99F /* AttachmentCaptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34080EFF22282C880087E99F /* AttachmentCaptionViewController.swift */; };
|
||||
34080F02222853E30087E99F /* ImageEditorBrushViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34080F01222853E30087E99F /* ImageEditorBrushViewController.swift */; };
|
||||
34080F04222858DC0087E99F /* OWSViewController+ImageEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34080F03222858DC0087E99F /* OWSViewController+ImageEditor.swift */; };
|
||||
340872BF22393CFA00CB25B0 /* UIGestureRecognizer+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340872BE22393CF900CB25B0 /* UIGestureRecognizer+OWS.swift */; };
|
||||
340B02BA1FA0D6C700F9CFEC /* ConversationViewItemTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 340B02B91FA0D6C700F9CFEC /* ConversationViewItemTest.m */; };
|
||||
340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */; };
|
||||
340FC8AA204DAC8D007AEB0F /* NotificationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC87C204DAC8C007AEB0F /* NotificationSettingsViewController.m */; };
|
||||
|
@ -644,6 +645,7 @@
|
|||
34080EFF22282C880087E99F /* AttachmentCaptionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentCaptionViewController.swift; sourceTree = "<group>"; };
|
||||
34080F01222853E30087E99F /* ImageEditorBrushViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageEditorBrushViewController.swift; sourceTree = "<group>"; };
|
||||
34080F03222858DC0087E99F /* OWSViewController+ImageEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OWSViewController+ImageEditor.swift"; sourceTree = "<group>"; };
|
||||
340872BE22393CF900CB25B0 /* UIGestureRecognizer+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+OWS.swift"; sourceTree = "<group>"; };
|
||||
340B02B61F9FD31800F9CFEC /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = translations/he.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
340B02B91FA0D6C700F9CFEC /* ConversationViewItemTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConversationViewItemTest.m; sourceTree = "<group>"; };
|
||||
340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsOptionsViewController.m; sourceTree = "<group>"; };
|
||||
|
@ -1585,12 +1587,12 @@
|
|||
4C948FF62146EB4800349F0D /* BlockListCache.swift */,
|
||||
343D3D991E9283F100165CA4 /* BlockListUIUtils.h */,
|
||||
343D3D9A1E9283F100165CA4 /* BlockListUIUtils.m */,
|
||||
451777C71FD61554001225FF /* FullTextSearcher.swift */,
|
||||
3466087120E550F300AFFE73 /* ConversationStyle.swift */,
|
||||
34480B4D1FD0A7A300BC14EF /* DebugLogger.h */,
|
||||
34480B4E1FD0A7A300BC14EF /* DebugLogger.m */,
|
||||
348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */,
|
||||
344F248C2007CCD600CFB4F4 /* DisplayableText.swift */,
|
||||
451777C71FD61554001225FF /* FullTextSearcher.swift */,
|
||||
346129AC1FD1F34E00532771 /* ImageCache.swift */,
|
||||
34BEDB1421C80BC9007B0EAE /* OWSAnyTouchGestureRecognizer.h */,
|
||||
34BEDB1521C80BCA007B0EAE /* OWSAnyTouchGestureRecognizer.m */,
|
||||
|
@ -1617,6 +1619,7 @@
|
|||
45360B8C1F9521F800FA666C /* Searcher.swift */,
|
||||
346129BD1FD2068600532771 /* ThreadUtil.h */,
|
||||
346129BE1FD2068600532771 /* ThreadUtil.m */,
|
||||
340872BE22393CF900CB25B0 /* UIGestureRecognizer+OWS.swift */,
|
||||
4C858A51212DC5E1001B45D3 /* UIImage+OWS.swift */,
|
||||
B97940251832BD2400BD66CB /* UIUtil.h */,
|
||||
B97940261832BD2400BD66CB /* UIUtil.m */,
|
||||
|
@ -3416,6 +3419,7 @@
|
|||
346941A3215D2EE400B5BFAD /* Theme.m in Sources */,
|
||||
4C23A5F2215C4ADE00534937 /* SheetViewController.swift in Sources */,
|
||||
34BBC84D220B2D0800857249 /* ImageEditorPinchGestureRecognizer.swift in Sources */,
|
||||
340872BF22393CFA00CB25B0 /* UIGestureRecognizer+OWS.swift in Sources */,
|
||||
34080F02222853E30087E99F /* ImageEditorBrushViewController.swift in Sources */,
|
||||
34AC0A14211B39EA00997B47 /* ContactCellView.m in Sources */,
|
||||
34AC0A15211B39EA00997B47 /* ContactsViewHelper.m in Sources */,
|
||||
|
|
|
@ -214,7 +214,6 @@ class ImageEditorCropViewController: OWSViewController {
|
|||
}
|
||||
|
||||
public func updateNavigationBar() {
|
||||
// TODO: Change this asset.
|
||||
let resetButton = navigationBarButton(imageName: "image_editor_undo",
|
||||
selector: #selector(didTapReset(sender:)))
|
||||
let doneButton = navigationBarButton(imageName: "image_editor_checkmark_full",
|
||||
|
|
|
@ -54,6 +54,10 @@ public class ImageEditorColor: NSObject {
|
|||
return color.cgColor
|
||||
})
|
||||
}
|
||||
|
||||
static func ==(left: ImageEditorColor, right: ImageEditorColor) -> Bool {
|
||||
return left.palettePhase.fuzzyEquals(right.palettePhase)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
|
|
@ -131,42 +131,54 @@ public class ImageEditorTextItem: ImageEditorItem {
|
|||
}
|
||||
|
||||
@objc
|
||||
public func copy(withUnitCenter newUnitCenter: CGPoint) -> ImageEditorTextItem {
|
||||
public func copy(unitCenter: CGPoint) -> ImageEditorTextItem {
|
||||
return ImageEditorTextItem(itemId: itemId,
|
||||
text: text,
|
||||
color: color,
|
||||
font: font,
|
||||
fontReferenceImageWidth: fontReferenceImageWidth,
|
||||
unitCenter: newUnitCenter,
|
||||
unitCenter: unitCenter,
|
||||
unitWidth: unitWidth,
|
||||
rotationRadians: rotationRadians,
|
||||
scaling: scaling)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func copy(withUnitCenter newUnitCenter: CGPoint,
|
||||
scaling newScaling: CGFloat,
|
||||
rotationRadians newRotationRadians: CGFloat) -> ImageEditorTextItem {
|
||||
public func copy(scaling: CGFloat,
|
||||
rotationRadians: CGFloat) -> ImageEditorTextItem {
|
||||
return ImageEditorTextItem(itemId: itemId,
|
||||
text: text,
|
||||
color: color,
|
||||
font: font,
|
||||
fontReferenceImageWidth: fontReferenceImageWidth,
|
||||
unitCenter: newUnitCenter,
|
||||
unitCenter: unitCenter,
|
||||
unitWidth: unitWidth,
|
||||
rotationRadians: newRotationRadians,
|
||||
scaling: newScaling)
|
||||
rotationRadians: rotationRadians,
|
||||
scaling: scaling)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func copy(withUnitCenter newUnitCenter: CGPoint, unitWidth newUnitWidth: CGFloat) -> ImageEditorTextItem {
|
||||
public func copy(unitWidth: CGFloat) -> ImageEditorTextItem {
|
||||
return ImageEditorTextItem(itemId: itemId,
|
||||
text: text,
|
||||
color: color,
|
||||
font: font,
|
||||
fontReferenceImageWidth: fontReferenceImageWidth,
|
||||
unitCenter: newUnitCenter,
|
||||
unitWidth: newUnitWidth,
|
||||
unitCenter: unitCenter,
|
||||
unitWidth: unitWidth,
|
||||
rotationRadians: rotationRadians,
|
||||
scaling: scaling)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func copy(font: UIFont) -> ImageEditorTextItem {
|
||||
return ImageEditorTextItem(itemId: itemId,
|
||||
text: text,
|
||||
color: color,
|
||||
font: font,
|
||||
fontReferenceImageWidth: fontReferenceImageWidth,
|
||||
unitCenter: unitCenter,
|
||||
unitWidth: unitWidth,
|
||||
rotationRadians: rotationRadians,
|
||||
scaling: scaling)
|
||||
}
|
||||
|
@ -174,4 +186,16 @@ public class ImageEditorTextItem: ImageEditorItem {
|
|||
public override func outputScale() -> CGFloat {
|
||||
return scaling
|
||||
}
|
||||
|
||||
static func ==(left: ImageEditorTextItem, right: ImageEditorTextItem) -> Bool {
|
||||
return (left.text == right.text &&
|
||||
left.color == right.color &&
|
||||
left.font.fontName == right.font.fontName &&
|
||||
left.font.pointSize.fuzzyEquals(right.font.pointSize) &&
|
||||
left.fontReferenceImageWidth.fuzzyEquals(right.fontReferenceImageWidth) &&
|
||||
left.unitCenter.fuzzyEquals(right.unitCenter) &&
|
||||
left.unitWidth.fuzzyEquals(right.unitWidth) &&
|
||||
left.rotationRadians.fuzzyEquals(right.rotationRadians) &&
|
||||
left.scaling.fuzzyEquals(right.scaling))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,8 @@ private class VAlignTextView: UITextView {
|
|||
|
||||
@objc
|
||||
public protocol ImageEditorTextViewControllerDelegate: class {
|
||||
func textEditDidComplete(textItem: ImageEditorTextItem, text: String?, color: ImageEditorColor)
|
||||
func textEditDidComplete(textItem: ImageEditorTextItem)
|
||||
func textEditDidDelete(textItem: ImageEditorTextItem)
|
||||
func textEditDidCancel()
|
||||
}
|
||||
|
||||
|
@ -195,6 +196,10 @@ public class ImageEditorTextViewController: OWSViewController, VAlignTextViewDel
|
|||
// This will determine the text view's size.
|
||||
paletteView.autoPinEdge(.leading, to: .trailing, of: textView, withOffset: 0)
|
||||
|
||||
let pinchGestureRecognizer = ImageEditorPinchGestureRecognizer(target: self, action: #selector(handlePinchGesture(_:)))
|
||||
pinchGestureRecognizer.referenceView = view
|
||||
view.addGestureRecognizer(pinchGestureRecognizer)
|
||||
|
||||
updateNavigationBar()
|
||||
}
|
||||
|
||||
|
@ -230,6 +235,35 @@ public class ImageEditorTextViewController: OWSViewController, VAlignTextViewDel
|
|||
updateNavigationBar(navigationBarItems: navigationBarItems)
|
||||
}
|
||||
|
||||
// MARK: - Pinch Gesture
|
||||
|
||||
private var pinchFontStart: UIFont?
|
||||
|
||||
@objc
|
||||
public func handlePinchGesture(_ gestureRecognizer: ImageEditorPinchGestureRecognizer) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
switch gestureRecognizer.state {
|
||||
case .began:
|
||||
pinchFontStart = textView.font
|
||||
case .changed, .ended:
|
||||
guard let pinchFontStart = pinchFontStart else {
|
||||
return
|
||||
}
|
||||
var pointSize: CGFloat = pinchFontStart.pointSize
|
||||
if gestureRecognizer.pinchStateLast.distance > 0 {
|
||||
pointSize *= gestureRecognizer.pinchStateLast.distance / gestureRecognizer.pinchStateStart.distance
|
||||
}
|
||||
let minPointSize: CGFloat = 12
|
||||
let maxPointSize: CGFloat = 64
|
||||
pointSize = max(minPointSize, min(maxPointSize, pointSize))
|
||||
let font = pinchFontStart.withSize(pointSize)
|
||||
textView.font = font
|
||||
default:
|
||||
pinchFontStart = nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Events
|
||||
|
||||
@objc func didTapUndo(sender: UIButton) {
|
||||
|
@ -268,14 +302,39 @@ public class ImageEditorTextViewController: OWSViewController, VAlignTextViewDel
|
|||
imageSize: model.srcImageSizePixels,
|
||||
transform: model.currentTransform())
|
||||
let unitWidth = textView.width() / imageFrame.width
|
||||
|
||||
newTextItem = textItem.copy(withUnitCenter: textCenterImageUnit, unitWidth: unitWidth)
|
||||
newTextItem = textItem.copy(unitCenter: textCenterImageUnit).copy(unitWidth: unitWidth)
|
||||
}
|
||||
|
||||
var font = textItem.font
|
||||
if let newFont = textView.font {
|
||||
font = newFont
|
||||
} else {
|
||||
owsFailDebug("Missing font.")
|
||||
}
|
||||
newTextItem = newTextItem.copy(font: font)
|
||||
|
||||
guard let text = textView.text?.ows_stripped(),
|
||||
text.count > 0 else {
|
||||
self.delegate?.textEditDidDelete(textItem: textItem)
|
||||
|
||||
self.dismiss(animated: false) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
newTextItem = newTextItem.copy(withText: text, color: paletteView.selectedValue)
|
||||
|
||||
// Hide the text view immediately to avoid animation glitches in the dismiss transition.
|
||||
textView.isHidden = true
|
||||
|
||||
self.delegate?.textEditDidComplete(textItem: newTextItem, text: textView.text, color: paletteView.selectedValue)
|
||||
if textItem == newTextItem {
|
||||
// No changes were made. Cancel to avoid dirtying the undo stack.
|
||||
self.delegate?.textEditDidCancel()
|
||||
} else {
|
||||
self.delegate?.textEditDidComplete(textItem: newTextItem)
|
||||
}
|
||||
|
||||
self.dismiss(animated: false) {
|
||||
// Do nothing.
|
||||
|
|
|
@ -260,9 +260,8 @@ public class ImageEditorView: UIView {
|
|||
|
||||
let newRotationRadians = textItem.rotationRadians + gestureRecognizer.pinchStateLast.angleRadians - gestureRecognizer.pinchStateStart.angleRadians
|
||||
|
||||
let newItem = textItem.copy(withUnitCenter: unitCenter,
|
||||
scaling: newScaling,
|
||||
rotationRadians: newRotationRadians)
|
||||
let newItem = textItem.copy(unitCenter: unitCenter).copy(scaling: newScaling,
|
||||
rotationRadians: newRotationRadians)
|
||||
|
||||
if pinchHasChanged {
|
||||
model.replace(item: newItem, suppressUndo: true)
|
||||
|
@ -348,7 +347,7 @@ public class ImageEditorView: UIView {
|
|||
transform: self.model.currentTransform())
|
||||
let gestureDeltaImageUnit = gestureNowImageUnit.minus(gestureStartImageUnit)
|
||||
let unitCenter = CGPointClamp01(movingTextStartUnitCenter.plus(gestureDeltaImageUnit))
|
||||
let newItem = textItem.copy(withUnitCenter: unitCenter)
|
||||
let newItem = textItem.copy(unitCenter: unitCenter)
|
||||
|
||||
if movingTextHasMoved {
|
||||
model.replace(item: newItem, suppressUndo: true)
|
||||
|
@ -515,26 +514,25 @@ extension ImageEditorView: ImageEditorModelObserver {
|
|||
|
||||
extension ImageEditorView: ImageEditorTextViewControllerDelegate {
|
||||
|
||||
public func textEditDidComplete(textItem: ImageEditorTextItem, text: String?, color: ImageEditorColor) {
|
||||
public func textEditDidComplete(textItem: ImageEditorTextItem) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let text = text?.ows_stripped(),
|
||||
text.count > 0 else {
|
||||
if model.has(itemForId: textItem.itemId) {
|
||||
model.remove(item: textItem)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Model items are immutable; we _replace_ the item rather than modify it.
|
||||
let newItem = textItem.copy(withText: text, color: color)
|
||||
if model.has(itemForId: textItem.itemId) {
|
||||
model.replace(item: newItem, suppressUndo: false)
|
||||
model.replace(item: textItem, suppressUndo: false)
|
||||
} else {
|
||||
model.append(item: newItem)
|
||||
model.append(item: textItem)
|
||||
}
|
||||
|
||||
self.currentColor = color
|
||||
self.currentColor = textItem.color
|
||||
}
|
||||
|
||||
public func textEditDidDelete(textItem: ImageEditorTextItem) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
if model.has(itemForId: textItem.itemId) {
|
||||
model.remove(item: textItem)
|
||||
}
|
||||
}
|
||||
|
||||
public func textEditDidCancel() {
|
||||
|
|
|
@ -145,6 +145,10 @@ public extension CGFloat {
|
|||
}
|
||||
|
||||
public static let halfPi: CGFloat = CGFloat.pi * 0.5
|
||||
|
||||
public func fuzzyEquals(_ other: CGFloat, tolerance: CGFloat = 0.001) -> Bool {
|
||||
return abs(self - other) < tolerance
|
||||
}
|
||||
}
|
||||
|
||||
public extension Int {
|
||||
|
@ -213,6 +217,11 @@ public extension CGPoint {
|
|||
public func applyingInverse(_ transform: CGAffineTransform) -> CGPoint {
|
||||
return applying(transform.inverted())
|
||||
}
|
||||
|
||||
public func fuzzyEquals(_ other: CGPoint, tolerance: CGFloat = 0.001) -> Bool {
|
||||
return (x.fuzzyEquals(other.x, tolerance: tolerance) &&
|
||||
y.fuzzyEquals(other.y, tolerance: tolerance))
|
||||
}
|
||||
}
|
||||
|
||||
public extension CGRect {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension UIGestureRecognizer {
|
||||
@objc
|
||||
public var stateString: String {
|
||||
return NSStringForUIGestureRecognizerState(state)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue