Commit 7f344994 by Daniel Dahan Committed by GitHub

Merge pull request #1130 from OrkhanAlikhanov/editor

Addressed multiple issues
parents c2ef0806 dbf37c44
...@@ -173,6 +173,7 @@ ...@@ -173,6 +173,7 @@
9D054A6520D175AC00D0528D /* Material+UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6320D175AC00D0528D /* Material+UIButton.swift */; }; 9D054A6520D175AC00D0528D /* Material+UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6320D175AC00D0528D /* Material+UIButton.swift */; };
9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; }; 9D054A6620D175AC00D0528D /* Material+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D054A6420D175AC00D0528D /* Material+UILabel.swift */; };
9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */; }; 9D39A81B20FE8ED100BA8FA1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */; };
9D9089B92118914500605DC9 /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9089B82118914500605DC9 /* Editor.swift */; };
9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */; }; 9DE84D721FF0252600586C8B /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */; };
9DE84D731FF0252600586C8B /* BaseButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */; }; 9DE84D731FF0252600586C8B /* BaseButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */; };
9DE84D741FF0252600586C8B /* CheckButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */; }; 9DE84D741FF0252600586C8B /* CheckButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */; };
...@@ -294,6 +295,7 @@ ...@@ -294,6 +295,7 @@
9D054A6320D175AC00D0528D /* Material+UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIButton.swift"; sourceTree = "<group>"; }; 9D054A6320D175AC00D0528D /* Material+UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIButton.swift"; sourceTree = "<group>"; };
9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = "<group>"; }; 9D054A6420D175AC00D0528D /* Material+UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UILabel.swift"; sourceTree = "<group>"; };
9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; }; 9D39A81A20FE8ED100BA8FA1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
9D9089B82118914500605DC9 /* Editor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = "<group>"; };
9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = "<group>"; }; 9DE84D6F1FF0252500586C8B /* RadioButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = "<group>"; };
9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseButtonGroup.swift; sourceTree = "<group>"; }; 9DE84D701FF0252500586C8B /* BaseButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseButtonGroup.swift; sourceTree = "<group>"; };
9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckButtonGroup.swift; sourceTree = "<group>"; }; 9DE84D711FF0252500586C8B /* CheckButtonGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckButtonGroup.swift; sourceTree = "<group>"; };
...@@ -321,6 +323,7 @@ ...@@ -321,6 +323,7 @@
96BCB79C1CB40DC500C806FE /* TextField.swift */, 96BCB79C1CB40DC500C806FE /* TextField.swift */,
961F18E71CD93E3E008927C5 /* ErrorTextField.swift */, 961F18E71CD93E3E008927C5 /* ErrorTextField.swift */,
9DF58CED20C098C60098968D /* ErrorTextFieldValidator.swift */, 9DF58CED20C098C60098968D /* ErrorTextFieldValidator.swift */,
9D9089B82118914500605DC9 /* Editor.swift */,
); );
name = Text; name = Text;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -978,6 +981,7 @@ ...@@ -978,6 +981,7 @@
965E811B1DD4D5C800D61E4B /* Switch.swift in Sources */, 965E811B1DD4D5C800D61E4B /* Switch.swift in Sources */,
965E811C1DD4D5C800D61E4B /* TabBar.swift in Sources */, 965E811C1DD4D5C800D61E4B /* TabBar.swift in Sources */,
965E811D1DD4D5C800D61E4B /* TableViewCell.swift in Sources */, 965E811D1DD4D5C800D61E4B /* TableViewCell.swift in Sources */,
9D9089B92118914500605DC9 /* Editor.swift in Sources */,
965E811E1DD4D5C800D61E4B /* TextField.swift in Sources */, 965E811E1DD4D5C800D61E4B /* TextField.swift in Sources */,
965E811F1DD4D5C800D61E4B /* ErrorTextField.swift in Sources */, 965E811F1DD4D5C800D61E4B /* ErrorTextField.swift in Sources */,
965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */, 965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */,
......
...@@ -64,9 +64,14 @@ public protocol TextFieldDelegate: UITextFieldDelegate { ...@@ -64,9 +64,14 @@ public protocol TextFieldDelegate: UITextFieldDelegate {
} }
open class TextField: UITextField { open class TextField: UITextField {
/// Minimum TextField text height.
private let minimumTextHeight: CGFloat = 32
/// Default size when using AutoLayout. /// Default size when using AutoLayout.
open override var intrinsicContentSize: CGSize { open override var intrinsicContentSize: CGSize {
return CGSize(width: bounds.width, height: max(32, super.intrinsicContentSize.height)) let h = textInsets.top + textInsets.bottom + minimumTextHeight
return CGSize(width: bounds.width, height: max(h, super.intrinsicContentSize.height))
} }
/// A Boolean that indicates if the placeholder label is animated. /// A Boolean that indicates if the placeholder label is animated.
...@@ -259,13 +264,9 @@ open class TextField: UITextField { ...@@ -259,13 +264,9 @@ open class TextField: UITextField {
/// Handles the textAlignment of the placeholderLabel. /// Handles the textAlignment of the placeholderLabel.
open override var textAlignment: NSTextAlignment { open override var textAlignment: NSTextAlignment {
get { didSet {
return super.textAlignment placeholderLabel.textAlignment = textAlignment
} detailLabel.textAlignment = textAlignment
set(value) {
super.textAlignment = value
placeholderLabel.textAlignment = value
detailLabel.textAlignment = value
} }
} }
...@@ -420,13 +421,17 @@ open class TextField: UITextField { ...@@ -420,13 +421,17 @@ open class TextField: UITextField {
/// EdgeInsets for text. /// EdgeInsets for text.
@objc @objc
open var textInset: CGFloat = 0 open var textInsets: EdgeInsets = .zero
/// EdgeInsets preset property for text.
open var textInsetsPreset = EdgeInsetsPreset.none {
didSet {
textInsets = EdgeInsetsPresetToValue(preset: textInsetsPreset)
}
}
open override func textRect(forBounds bounds: CGRect) -> CGRect { open override func textRect(forBounds bounds: CGRect) -> CGRect {
var b = super.textRect(forBounds: bounds) return super.textRect(forBounds: bounds).inset(by: textInsets)
b.origin.x += textInset
b.size.width -= textInset
return b
} }
open override func editingRect(forBounds bounds: CGRect) -> CGRect { open override func editingRect(forBounds bounds: CGRect) -> CGRect {
...@@ -573,26 +578,29 @@ fileprivate extension TextField { ...@@ -573,26 +578,29 @@ fileprivate extension TextField {
fileprivate extension TextField { fileprivate extension TextField {
/// Layout the placeholderLabel. /// Layout the placeholderLabel.
func layoutPlaceholderLabel() { func layoutPlaceholderLabel() {
let x = leftViewWidth + textInset let leftPadding = leftViewWidth + textInsets.left
let h = 0 == bounds.height ? intrinsicContentSize.height : bounds.height let w = bounds.width - leftPadding - textInsets.right
let w = bounds.width - leftViewWidth - 2 * textInset var h = placeholderLabel.sizeThatFits(CGSize(width: w, height: .greatestFiniteMagnitude)).height
placeholderLabel.frame.size = CGSize(width: w, height: h) h = min(h, bounds.height - textInsets.top - textInsets.bottom)
h = max(h, minimumTextHeight)
placeholderLabel.bounds.size = CGSize(width: w, height: h)
guard isEditing || !isEmpty || !isPlaceholderAnimated else { guard isEditing || !isEmpty || !isPlaceholderAnimated else {
placeholderLabel.transform = CGAffineTransform.identity placeholderLabel.transform = CGAffineTransform.identity
placeholderLabel.frame.origin = CGPoint(x: x, y: 0) placeholderLabel.frame.origin = CGPoint(x: leftPadding, y: textInsets.top)
return return
} }
placeholderLabel.transform = CGAffineTransform(scaleX: placeholderActiveScale, y: placeholderActiveScale) placeholderLabel.transform = CGAffineTransform(scaleX: placeholderActiveScale, y: placeholderActiveScale)
placeholderLabel.frame.origin.y = -placeholderLabel.frame.height + placeholderVerticalOffset placeholderLabel.frame.origin.y = -placeholderLabel.frame.height + placeholderVerticalOffset
switch textAlignment { switch placeholderLabel.textAlignment {
case .left, .natural: case .left, .natural:
placeholderLabel.frame.origin.x = x + placeholderHorizontalOffset placeholderLabel.frame.origin.x = leftPadding + placeholderHorizontalOffset
case .right: case .right:
placeholderLabel.frame.origin.x = (bounds.width * (1.0 - placeholderActiveScale)) - textInset + placeholderHorizontalOffset let scaledWidth = w * placeholderActiveScale
placeholderLabel.frame.origin.x = bounds.width - scaledWidth - textInsets.right + placeholderHorizontalOffset
default:break default:break
} }
} }
......
...@@ -94,7 +94,7 @@ open class TextView: UITextView { ...@@ -94,7 +94,7 @@ open class TextView: UITextView {
} }
/// A boolean indicating whether the text is in edit mode. /// A boolean indicating whether the text is in edit mode.
open fileprivate(set) var isEditing = true open fileprivate(set) var isEditing = false
/// Is the keyboard hidden. /// Is the keyboard hidden.
open fileprivate(set) var isKeyboardHidden = true open fileprivate(set) var isKeyboardHidden = true
...@@ -134,6 +134,9 @@ open class TextView: UITextView { ...@@ -134,6 +134,9 @@ open class TextView: UITextView {
@IBInspectable @IBInspectable
public let placeholderLabel = UILabel() public let placeholderLabel = UILabel()
/// A property to enable/disable operations on the placeholderLabel
internal var isPlaceholderLabelEnabled: Bool = true
/// Placeholder normal text /// Placeholder normal text
@IBInspectable @IBInspectable
open var placeholderColor = Color.darkText.others { open var placeholderColor = Color.darkText.others {
...@@ -159,6 +162,13 @@ open class TextView: UITextView { ...@@ -159,6 +162,13 @@ open class TextView: UITextView {
} }
} }
/// Handles the textAlignment of the placeholderLabel and textView itself.
open override var textAlignment: NSTextAlignment {
didSet {
placeholderLabel.textAlignment = textAlignment
}
}
/** /**
An initializer that initializes the object with a NSCoder object. An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance. - Parameter aDecoder: A NSCoder instance.
...@@ -280,18 +290,70 @@ open class TextView: UITextView { ...@@ -280,18 +290,70 @@ open class TextView: UITextView {
prepareRegularExpression() prepareRegularExpression()
preparePlaceholderLabel() preparePlaceholderLabel()
} }
open override func insertText(_ text: String) { open override var contentSize: CGSize {
fixTypingFont() didSet {
super.insertText(text) guard isGrowEnabled else {
fixTypingFont() return
}
invalidateIntrinsicContentSize()
guard isEditing && isHeightChangeAnimated else {
superview?.layoutIfNeeded()
return
}
UIView.animate(withDuration: 0.15) {
let v = self.superview as? Editor ?? self
v.superview?.layoutIfNeeded()
}
} }
}
open override func paste(_ sender: Any?) {
fixTypingFont() /// A Boolean that indicates if the height change during growing is animated.
super.paste(sender) open var isHeightChangeAnimated = true
fixTypingFont()
/// Maximum preffered layout height before scrolling.
open var preferredMaxLayoutHeight: CGFloat = 0 {
didSet {
invalidateIntrinsicContentSize()
superview?.layoutIfNeeded()
} }
}
/// A property indicating if textView allowed to grow.
private var isGrowEnabled: Bool {
return preferredMaxLayoutHeight > 0 && isScrollEnabled
}
/// Minimum TextView text height.
internal let minimumTextHeight: CGFloat = 32
open override var intrinsicContentSize: CGSize {
guard isGrowEnabled else {
return super.intrinsicContentSize
}
let insets = textContainerInsets
let w = bounds.width - insets.left - insets.right - 2 * textContainer.lineFragmentPadding
let placeholderH = placeholderLabel.sizeThatFits(CGSize(width: w, height: .greatestFiniteMagnitude)).height
var h = max(minimumTextHeight, placeholderH) + insets.top + insets.bottom
h = max(h, contentSize.height)
return CGSize(width: UIView.noIntrinsicMetric, height: min(h, preferredMaxLayoutHeight))
}
open override func insertText(_ text: String) {
fixTypingFont()
super.insertText(text)
fixTypingFont()
}
open override func paste(_ sender: Any?) {
fixTypingFont()
super.paste(sender)
fixTypingFont()
}
} }
fileprivate extension TextView { fileprivate extension TextView {
...@@ -325,12 +387,20 @@ fileprivate extension TextView { ...@@ -325,12 +387,20 @@ fileprivate extension TextView {
fileprivate extension TextView { fileprivate extension TextView {
/// Updates the placeholderLabel text color. /// Updates the placeholderLabel text color.
func updatePlaceholderLabelColor() { func updatePlaceholderLabelColor() {
guard isPlaceholderLabelEnabled else {
return
}
tintColor = placeholderColor tintColor = placeholderColor
placeholderLabel.textColor = placeholderColor placeholderLabel.textColor = placeholderColor
} }
/// Updates the placeholderLabel visibility. /// Updates the placeholderLabel visibility.
func updatePlaceholderVisibility() { func updatePlaceholderVisibility() {
guard isPlaceholderLabelEnabled else {
return
}
placeholderLabel.isHidden = !isEmpty placeholderLabel.isHidden = !isEmpty
} }
} }
...@@ -338,15 +408,19 @@ fileprivate extension TextView { ...@@ -338,15 +408,19 @@ fileprivate extension TextView {
fileprivate extension TextView { fileprivate extension TextView {
/// Laysout the placeholder UILabel. /// Laysout the placeholder UILabel.
func layoutPlaceholderLabel() { func layoutPlaceholderLabel() {
placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2 guard isPlaceholderLabelEnabled else {
return
let x = textContainerInset.left + textContainer.lineFragmentPadding }
let y = textContainerInset.top
placeholderLabel.sizeToFit() let insets = textContainerInsets
let leftPadding = insets.left + textContainer.lineFragmentPadding
let rightPadding = insets.right + textContainer.lineFragmentPadding
let w = bounds.width - leftPadding - rightPadding
var h = placeholderLabel.sizeThatFits(CGSize(width: w, height: .greatestFiniteMagnitude)).height
h = max(h, minimumTextHeight)
h = min(h, bounds.height - insets.top - insets.bottom)
placeholderLabel.frame.origin.x = x placeholderLabel.frame = CGRect(x: leftPadding, y: insets.top, width: w, height: h)
placeholderLabel.frame.origin.y = y
placeholderLabel.frame.size.width = textContainer.size.width - textContainerInset.right - textContainer.lineFragmentPadding
} }
} }
......
...@@ -30,8 +30,6 @@ ...@@ -30,8 +30,6 @@
import UIKit import UIKit
fileprivate var ToolbarContext: UInt8 = 0
open class Toolbar: Bar { open class Toolbar: Bar {
/// A convenience property to set the titleLabel.text. /// A convenience property to set the titleLabel.text.
@IBInspectable @IBInspectable
...@@ -65,10 +63,6 @@ open class Toolbar: Bar { ...@@ -65,10 +63,6 @@ open class Toolbar: Bar {
@IBInspectable @IBInspectable
public let detailLabel = UILabel() public let detailLabel = UILabel()
deinit {
removeObserver(self, forKeyPath: #keyPath(titleLabel.textAlignment))
}
/** /**
An initializer that initializes the object with a NSCoder object. An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance. - Parameter aDecoder: A NSCoder instance.
...@@ -87,15 +81,6 @@ open class Toolbar: Bar { ...@@ -87,15 +81,6 @@ open class Toolbar: Bar {
super.init(frame: frame) super.init(frame: frame)
} }
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard "titleLabel.textAlignment" == keyPath else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
contentViewAlignment = .center == titleLabel.textAlignment ? .center : .full
}
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
guard willLayout else { guard willLayout else {
...@@ -143,16 +128,21 @@ open class Toolbar: Bar { ...@@ -143,16 +128,21 @@ open class Toolbar: Bar {
prepareTitleLabel() prepareTitleLabel()
prepareDetailLabel() prepareDetailLabel()
} }
/// A reference to titleLabel.textAlignment observation.
private var titleLabelTextAlignmentObserver: NSKeyValueObservation!
} }
fileprivate extension Toolbar { private extension Toolbar {
/// Prepares the titleLabel. /// Prepares the titleLabel.
func prepareTitleLabel() { func prepareTitleLabel() {
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
titleLabel.contentScaleFactor = Screen.scale titleLabel.contentScaleFactor = Screen.scale
titleLabel.font = RobotoFont.medium(with: 17) titleLabel.font = RobotoFont.medium(with: 17)
titleLabel.textColor = Color.darkText.primary titleLabel.textColor = Color.darkText.primary
addObserver(self, forKeyPath: #keyPath(titleLabel.textAlignment), options: [], context: &ToolbarContext) titleLabelTextAlignmentObserver = titleLabel.observe(\.textAlignment) { [weak self] titleLabel, _ in
self?.contentViewAlignment = .center == titleLabel.textAlignment ? .center : .full
}
} }
/// Prepares the detailLabel. /// Prepares the detailLabel.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment