Commit dc116e17 by Daniel Dahan

development: removed Editor as now TextView is self contained

parent 09b0f7a4
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
9617B0811DFCA8CF00410F8F /* Capture.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B0D1DBE6AF600DA84DB /* Capture.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9617B0811DFCA8CF00410F8F /* Capture.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B0D1DBE6AF600DA84DB /* Capture.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0821DFCA8CF00410F8F /* CapturePreview.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B0F1DBE6AF600DA84DB /* CapturePreview.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9617B0821DFCA8CF00410F8F /* CapturePreview.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B0F1DBE6AF600DA84DB /* CapturePreview.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0831DFCA8CF00410F8F /* CaptureController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B0E1DBE6AF600DA84DB /* CaptureController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9617B0831DFCA8CF00410F8F /* CaptureController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B0E1DBE6AF600DA84DB /* CaptureController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0841DFCA8CF00410F8F /* Editor.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961DED451DCC40C500F425B6 /* Editor.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0861DFCA8CF00410F8F /* HeightPreset.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9626CB9A1DAD3D1D003E2611 /* HeightPreset.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9617B0861DFCA8CF00410F8F /* HeightPreset.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9626CB9A1DAD3D1D003E2611 /* HeightPreset.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0871DFCA8CF00410F8F /* PageTabBarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963FBF071D669D14008F8512 /* PageTabBarController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9617B0871DFCA8CF00410F8F /* PageTabBarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963FBF071D669D14008F8512 /* PageTabBarController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B0881DFCA8CF00410F8F /* PhotoLibrary.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B161DBE6B1800DA84DB /* PhotoLibrary.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9617B0881DFCA8CF00410F8F /* PhotoLibrary.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B161DBE6B1800DA84DB /* PhotoLibrary.swift */; settings = {ATTRIBUTES = (Public, ); }; };
...@@ -116,7 +115,6 @@ ...@@ -116,7 +115,6 @@
965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79D1CB40DC500C806FE /* TextStorage.swift */; }; 965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79D1CB40DC500C806FE /* TextStorage.swift */; };
965E81221DD4D5C800D61E4B /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79E1CB40DC500C806FE /* TextView.swift */; }; 965E81221DD4D5C800D61E4B /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79E1CB40DC500C806FE /* TextView.swift */; };
965E81231DD4D7C800D61E4B /* BottomTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7591CB40DC500C806FE /* BottomTabBar.swift */; }; 965E81231DD4D7C800D61E4B /* BottomTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7591CB40DC500C806FE /* BottomTabBar.swift */; };
965E81241DD4D7C800D61E4B /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961DED451DCC40C500F425B6 /* Editor.swift */; };
965E81261DD4D7C800D61E4B /* CharacterAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961276621DCD8B1800A7D920 /* CharacterAttribute.swift */; }; 965E81261DD4D7C800D61E4B /* CharacterAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961276621DCD8B1800A7D920 /* CharacterAttribute.swift */; };
9697F7BF1D8F2572004741EC /* Divider.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96230AB71D6A520C00AF47DC /* Divider.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9697F7BF1D8F2572004741EC /* Divider.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96230AB71D6A520C00AF47DC /* Divider.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C01D8F2572004741EC /* Material+CALayer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96F1DC871D654FDF0025F925 /* Material+CALayer.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9697F7C01D8F2572004741EC /* Material+CALayer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96F1DC871D654FDF0025F925 /* Material+CALayer.swift */; settings = {ATTRIBUTES = (Public, ); }; };
...@@ -193,7 +191,6 @@ ...@@ -193,7 +191,6 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
961276621DCD8B1800A7D920 /* CharacterAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterAttribute.swift; sourceTree = "<group>"; }; 961276621DCD8B1800A7D920 /* CharacterAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterAttribute.swift; sourceTree = "<group>"; };
961730591E145DE900A9A297 /* CollectionViewCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCard.swift; sourceTree = "<group>"; }; 961730591E145DE900A9A297 /* CollectionViewCard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCard.swift; sourceTree = "<group>"; };
961DED451DCC40C500F425B6 /* Editor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = "<group>"; };
961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; 961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
961E6BE11DDA2AF3004E6C93 /* Screen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; }; 961E6BE11DDA2AF3004E6C93 /* Screen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; };
961EFC571D738FF600E84652 /* SnackbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnackbarController.swift; sourceTree = "<group>"; }; 961EFC571D738FF600E84652 /* SnackbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnackbarController.swift; sourceTree = "<group>"; };
...@@ -308,14 +305,6 @@ ...@@ -308,14 +305,6 @@
name = TextField; name = TextField;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
961DED441DCC40AC00F425B6 /* Editor */ = {
isa = PBXGroup;
children = (
961DED451DCC40C500F425B6 /* Editor.swift */,
);
name = Editor;
sourceTree = "<group>";
};
961E6BDD1DDA2A7E004E6C93 /* Application */ = { 961E6BDD1DDA2A7E004E6C93 /* Application */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -526,7 +515,6 @@ ...@@ -526,7 +515,6 @@
96328B9A1E05C135009A4C90 /* Data */, 96328B9A1E05C135009A4C90 /* Data */,
96BCB80B1CB410CC00C806FE /* Device */, 96BCB80B1CB410CC00C806FE /* Device */,
96230AB61D6A51FD00AF47DC /* Divider */, 96230AB61D6A51FD00AF47DC /* Divider */,
961DED441DCC40AC00F425B6 /* Editor */,
96BCB80A1CB410A100C806FE /* Extension */, 96BCB80A1CB410A100C806FE /* Extension */,
96BCB8071CB4101C00C806FE /* Font */, 96BCB8071CB4101C00C806FE /* Font */,
9602F00C1DA1163000F3FB79 /* Grid */, 9602F00C1DA1163000F3FB79 /* Grid */,
...@@ -817,7 +805,6 @@ ...@@ -817,7 +805,6 @@
9617B0811DFCA8CF00410F8F /* Capture.swift in Headers */, 9617B0811DFCA8CF00410F8F /* Capture.swift in Headers */,
9617B0821DFCA8CF00410F8F /* CapturePreview.swift in Headers */, 9617B0821DFCA8CF00410F8F /* CapturePreview.swift in Headers */,
9617B0831DFCA8CF00410F8F /* CaptureController.swift in Headers */, 9617B0831DFCA8CF00410F8F /* CaptureController.swift in Headers */,
9617B0841DFCA8CF00410F8F /* Editor.swift in Headers */,
9617B0861DFCA8CF00410F8F /* HeightPreset.swift in Headers */, 9617B0861DFCA8CF00410F8F /* HeightPreset.swift in Headers */,
9617B0871DFCA8CF00410F8F /* PageTabBarController.swift in Headers */, 9617B0871DFCA8CF00410F8F /* PageTabBarController.swift in Headers */,
9617B0881DFCA8CF00410F8F /* PhotoLibrary.swift in Headers */, 9617B0881DFCA8CF00410F8F /* PhotoLibrary.swift in Headers */,
...@@ -923,7 +910,6 @@ ...@@ -923,7 +910,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
965E81231DD4D7C800D61E4B /* BottomTabBar.swift in Sources */, 965E81231DD4D7C800D61E4B /* BottomTabBar.swift in Sources */,
965E81241DD4D7C800D61E4B /* Editor.swift in Sources */,
961E6BE21DDA2AF3004E6C93 /* Screen.swift in Sources */, 961E6BE21DDA2AF3004E6C93 /* Screen.swift in Sources */,
965E81261DD4D7C800D61E4B /* CharacterAttribute.swift in Sources */, 965E81261DD4D7C800D61E4B /* CharacterAttribute.swift in Sources */,
965E80FF1DD4D5C800D61E4B /* BottomNavigationController.swift in Sources */, 965E80FF1DD4D5C800D61E4B /* BottomNavigationController.swift in Sources */,
......
...@@ -117,7 +117,7 @@ extension NSMutableAttributedString { ...@@ -117,7 +117,7 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values. - Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func addAttributes(characterAttributes: [CharacterAttribute : Any] = [:], range: NSRange) { open func addAttributes(characterAttributes: [CharacterAttribute: Any], range: NSRange) {
for (k, v) in characterAttributes { for (k, v) in characterAttributes {
addAttribute(characterAttribute: k, value: v, range: range) addAttribute(characterAttribute: k, value: v, range: range)
} }
...@@ -139,7 +139,7 @@ extension NSMutableAttributedString { ...@@ -139,7 +139,7 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values. - Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func updateAttributes(characterAttributes: [CharacterAttribute : Any] = [:], range: NSRange) { open func updateAttributes(characterAttributes: [CharacterAttribute: Any], range: NSRange) {
for (k, v) in characterAttributes { for (k, v) in characterAttributes {
updateAttribute(characterAttribute: k, value: v, range: range) updateAttribute(characterAttribute: k, value: v, range: range)
} }
...@@ -159,7 +159,7 @@ extension NSMutableAttributedString { ...@@ -159,7 +159,7 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: An Array of CharacterAttributes. - Parameter characterAttributes: An Array of CharacterAttributes.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func removeAttributes(characterAttributes: [CharacterAttribute] = [], range: NSRange) { open func removeAttributes(characterAttributes: [CharacterAttribute], range: NSRange) {
for k in characterAttributes { for k in characterAttributes {
removeAttribute(characterAttribute: k, range: range) removeAttribute(characterAttribute: k, range: range)
} }
......
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
@objc(EditorDelegate)
public protocol EditorDelegate {
/**
A delegation method that is executed when text will be
processed during editing.
- Parameter editor: An Editor.
- Parameter willProcessEditing textStorage: A TextStorage.
- Parameter text: A String.
- Parameter range: A NSRange.
*/
@objc
optional func editor(editor: Editor, willProcessEditing textStorage: TextStorage, text: String, range: NSRange)
/**
A delegation method that is executed when text has been
processed after editing.
- Parameter editor: An Editor.
- Parameter didProcessEditing textStorage: A TextStorage.
- Parameter text: A String.
- Parameter range: A NSRange.
*/
@objc
optional func editor(editor: Editor, didProcessEditing textStorage: TextStorage, text: String, range: NSRange)
/**
A delegation method that is executed when the textView should begin editing.
- Parameter editor: An Editor.
- Parameter shouldBeginEditing textView: A UITextView.
- Returns: A boolean indicating if the textView should begin editing, true if
yes, false otherwise.
*/
@objc
optional func editor(editor: Editor, shouldBeginEditing textView: UITextView) -> Bool
/**
A delegation method that is executed when the textView should end editing.
- Parameter editor: An Editor.
- Parameter shouldEndEditing textView: A UITextView.
- Returns: A boolean indicating if the textView should end editing, true if
yes, false otherwise.
*/
@objc
optional func editor(editor: Editor, shouldEndEditing textView: UITextView) -> Bool
/**
A delegation method that is executed when the textView did begin editing.
- Parameter editor: An Editor.
- Parameter didBeginEditing textView: A UITextView.
*/
@objc
optional func editor(editor: Editor, didBeginEditing textView: UITextView)
/**
A delegation method that is executed when the textView did begin editing.
- Parameter editor: An Editor.
- Parameter didBeginEditing textView: A UITextView.
*/
@objc
optional func editor(editor: Editor, didEndEditing textView: UITextView)
/**
A delegation method that is executed when the textView should change text in
a given range with replacement text.
- Parameter editor: An Editor.
- Parameter textView: A UITextView.
- Parameter shouldChangeTextIn range: A NSRange.
- Parameter replacementText text: A String.
- Returns: A boolean indicating if the textView should change text in a given
range with the given replacement text, true if yes, false otherwise.
*/
@objc
optional func editor(editor: Editor, textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
/**
A delegation method that is executed when the textView did change.
- Parameter editor: An Editor.
- Parameter didChange textView: A UITextView.
*/
@objc
optional func editor(editor: Editor, didChange textView: UITextView)
/**
A delegation method that is executed when the textView did change a selection.
- Parameter editor: An Editor.
- Parameter didChangeSelection textView: A UITextView.
*/
@objc
optional func editor(editor: Editor, didChangeSelection textView: UITextView)
/**
A delegation method that is executed when the textView should interact with
a URL in a given character range.
- Parameter editor: An Editor.
- Parameter textView: A UITextView.
- Parameter shouldInteractWith URL: A URL.
- Parameter in characterRange: A Range.
- Returns: A boolean indicating if the textView should interact with a URL in
a given character range, true if yes, false otherwise.
@available(iOS, introduced: 8.0, deprecated: 10.0)
@objc
optional func editor(editor: Editor, textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool
A delegation method that is executed when the textView should interact with
a text attachment in a given character range.
- Parameter editor: An Editor.
- Parameter textView: A UITextView.
- Parameter shouldInteractWith textAttachment: A NSTextAttachment.
- Parameter in characterRange: A Range.
- Returns: A boolean indicating if the textView should interact with a
NSTextAttachment in a given character range, true if yes, false otherwise.
@available(iOS, introduced: 8.0, deprecated: 10.0)
@objc
optional func editor(editor: Editor, textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool
A delegation method that is executed when the textView should interact with
a URL in a given character range.
- Parameter editor: An Editor.
- Parameter textView: A UITextView.
- Parameter shouldInteractWith URL: A URL.
- Parameter in characterRange: A Range.
- Parameter interaction: A UITextItemInteraction.
- Returns: A boolean indicating if the textView should interact with a URL in
a given character range, true if yes, false otherwise.
@available(iOS 10.0, *)
@objc
optional func editor(editor: Editor, textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool
A delegation method that is executed when the textView should interact with
a text attachment in a given character range.
- Parameter editor: An Editor.
- Parameter textView: A UITextView.
- Parameter shouldInteractWith textAttachment: A NSTextAttachment.
- Parameter in characterRange: A Range.
- Parameter interaction: A UITextItemInteraction.
- Returns: A boolean indicating if the textView should interact with a
NSTextAttachment in a given character range, true if yes, false otherwise.
@available(iOS 10.0, *)
@objc
optional func editor(editor: Editor, textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool
*/
/**
A delegation method that is executed when the keyboard will open.
- Parameter editor: An Editor.
- Parameter willShowKeyboard value: A NSValue.
*/
@objc
optional func editor(editor: Editor, willShowKeyboard value: NSValue)
/**
A delegation method that is executed when the keyboard will close.
- Parameter editor: An Editor.
- Parameter willHideKeyboard value: A NSValue.
*/
@objc
optional func editor(editor: Editor, willHideKeyboard value: NSValue)
}
open class Editor: View {
/// Will layout the view.
open var willLayout: Bool {
return 0 < width && 0 < height && nil != superview
}
/// Is the keyboard hidden.
open fileprivate(set) var isKeyboardHidden = true
/// TextStorage instance that is observed while editing.
open fileprivate(set) var textStorage: TextStorage!
/// A reference to the NSTextContainer.
open fileprivate(set) var textContainer: NSTextContainer!
/// A reference to the NSLayoutManager.
open fileprivate(set) var layoutManager: NSLayoutManager!
/// A reference to the textView inputAccessoryView.
@IBInspectable
open override var inputAccessoryView: UIView? {
get {
return textView.inputAccessoryView
}
set(value) {
textView.inputAccessoryView = value
}
}
/// A preset wrapper around textViewEdgeInsets.
open var textViewEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
textViewEdgeInsets = EdgeInsetsPresetToValue(preset: textViewEdgeInsetsPreset)
}
}
/// A reference to textViewEdgeInsets.
@IBInspectable
open var textViewEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/// Reference to the TextView.
open fileprivate(set) var textView: TextView!
/// The placeholderLabel font value.
@IBInspectable
open var font: UIFont? {
get {
return placeholderLabel.font
}
set(value) {
placeholderLabel.font = value
textView.font = value
}
}
/// The placeholderLabel text value.
@IBInspectable
open var placeholder: String? {
get {
return placeholderLabel.text
}
set(value) {
placeholderLabel.text = value
layoutSubviews()
}
}
/// The placeholder UILabel.
@IBInspectable
open let placeholderLabel = UILabel()
/// Placeholder normal text
@IBInspectable
open var placeholderNormalColor = Color.darkText.others {
didSet {
updatePlaceholderLabelColor()
}
}
/// Placeholder active text
@IBInspectable
open var placeholderActiveColor = Color.blue.base {
didSet {
updatePlaceholderLabelColor()
}
}
/// Placeholder UILabel EdgeInsets preset property.
open var placeholderLabelEdgeInsetsPreset = EdgeInsetsPreset.none {
didSet {
placeholderLabelEdgeInsets = EdgeInsetsPresetToValue(preset: placeholderLabelEdgeInsetsPreset)
}
}
/// Placeholder UILabel EdgeInsets property.
open var placeholderLabelEdgeInsets = EdgeInsets.zero {
didSet {
layoutSubviews()
}
}
/// An override to the text property.
@IBInspectable
open var text: String? {
get {
return textView.text
}
set(value) {
textView.text = value
}
}
/// An override to the attributedText property.
open var attributedText: NSAttributedString! {
get {
return textView.attributedText
}
set(value) {
textView.attributedText = value
}
}
/// A reference to an EditorDelegate.
open weak var delegate: EditorDelegate?
/// The string pattern to match within the textStorage.
open var pattern = "(^|\\s)#[\\d\\w_\u{203C}\u{2049}\u{20E3}\u{2122}\u{2139}\u{2194}-\u{2199}\u{21A9}-\u{21AA}\u{231A}-\u{231B}\u{23E9}-\u{23EC}\u{23F0}\u{23F3}\u{24C2}\u{25AA}-\u{25AB}\u{25B6}\u{25C0}\u{25FB}-\u{25FE}\u{2600}-\u{2601}\u{260E}\u{2611}\u{2614}-\u{2615}\u{261D}\u{263A}\u{2648}-\u{2653}\u{2660}\u{2663}\u{2665}-\u{2666}\u{2668}\u{267B}\u{267F}\u{2693}\u{26A0}-\u{26A1}\u{26AA}-\u{26AB}\u{26BD}-\u{26BE}\u{26C4}-\u{26C5}\u{26CE}\u{26D4}\u{26EA}\u{26F2}-\u{26F3}\u{26F5}\u{26FA}\u{26FD}\u{2702}\u{2705}\u{2708}-\u{270C}\u{270F}\u{2712}\u{2714}\u{2716}\u{2728}\u{2733}-\u{2734}\u{2744}\u{2747}\u{274C}\u{274E}\u{2753}-\u{2755}\u{2757}\u{2764}\u{2795}-\u{2797}\u{27A1}\u{27B0}\u{2934}-\u{2935}\u{2B05}-\u{2B07}\u{2B1B}-\u{2B1C}\u{2B50}\u{2B55}\u{3030}\u{303D}\u{3297}\u{3299}\u{1F004}\u{1F0CF}\u{1F170}-\u{1F171}\u{1F17E}-\u{1F17F}\u{1F18E}\u{1F191}-\u{1F19A}\u{1F1E7}-\u{1F1EC}\u{1F1EE}-\u{1F1F0}\u{1F1F3}\u{1F1F5}\u{1F1F7}-\u{1F1FA}\u{1F201}-\u{1F202}\u{1F21A}\u{1F22F}\u{1F232}-\u{1F23A}\u{1F250}-\u{1F251}\u{1F300}-\u{1F320}\u{1F330}-\u{1F335}\u{1F337}-\u{1F37C}\u{1F380}-\u{1F393}\u{1F3A0}-\u{1F3C4}\u{1F3C6}-\u{1F3CA}\u{1F3E0}-\u{1F3F0}\u{1F400}-\u{1F43E}\u{1F440}\u{1F442}-\u{1F4F7}\u{1F4F9}-\u{1F4FC}\u{1F500}-\u{1F507}\u{1F509}-\u{1F53D}\u{1F550}-\u{1F567}\u{1F5FB}-\u{1F640}\u{1F645}-\u{1F64F}\u{1F680}-\u{1F68A}]+" {
didSet {
prepareRegularExpression()
}
}
/**
A convenience property that accesses the textStorage
string.
*/
open var string: String {
return textStorage.string
}
/// An Array of matches that match the pattern expression.
open var matches: [String] {
return textStorage.expression!.matches(in: string, options: [], range: NSMakeRange(0, string.utf16.count)).map { [unowned self] in
(self.string as NSString).substring(with: $0.range).trimmed
}
}
/**
An Array of unique matches that match the pattern
expression.
*/
open var uniqueMatches: [String] {
var seen = [String: Bool]()
return matches.filter { nil == seen.updateValue(true, forKey: $0) }
}
open override func layoutSubviews() {
super.layoutSubviews()
guard willLayout else {
return
}
textView.frame = CGRect(x: textViewEdgeInsets.left, y: textViewEdgeInsets.top, width: width - textViewEdgeInsets.left - textViewEdgeInsets.right, height: height - textViewEdgeInsets.top - textViewEdgeInsets.bottom)
placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2
layout(placeholderLabel).edges(
top: placeholderLabelEdgeInsets.top,
left: placeholderLabelEdgeInsets.left + textContainer.lineFragmentPadding,
bottom: placeholderLabelEdgeInsets.bottom,
right: placeholderLabelEdgeInsets.right + textContainer.lineFragmentPadding)
}
/// Deinitializer.
deinit {
NotificationCenter.default.removeObserver(self)
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() {
super.prepare()
prepareTextContainer()
prepareLayoutManager()
prepareTextStorage()
prepareRegularExpression()
prepareTextView()
preparePlaceholderLabel()
prepareKeyboardNotificationObservers()
}
}
extension Editor {
/// Prepares the textContainer.
fileprivate func prepareTextContainer() {
textContainer = NSTextContainer(size: bounds.size)
}
/// Prepares the layoutManager.
fileprivate func prepareLayoutManager() {
layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer)
}
/// Prepares the textStorage.
fileprivate func prepareTextStorage() {
textStorage = TextStorage()
textStorage.addLayoutManager(layoutManager)
textStorage.delegate = self
}
/// Prepares the textView.
fileprivate func prepareTextView() {
textView = TextView(textContainer: textContainer)
textView.delegate = self
addSubview(textView)
}
/// Prepares the regular expression for matching.
fileprivate func prepareRegularExpression() {
textStorage.expression = try? NSRegularExpression(pattern: pattern, options: [])
}
/// prepares the placeholderLabel property.
fileprivate func preparePlaceholderLabel() {
placeholderLabel.font = font
placeholderLabel.textAlignment = textView.textAlignment
placeholderLabel.numberOfLines = 0
placeholderLabel.backgroundColor = .clear
}
/// Prepares the keyboard notification center observers.
fileprivate func prepareKeyboardNotificationObservers() {
let defaultCenter = NotificationCenter.default
defaultCenter.addObserver(self, selector: #selector(handleKeyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
defaultCenter.addObserver(self, selector: #selector(handleKeyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
defaultCenter.addObserver(textView, selector: #selector(handleTextViewTextDidBegin), name: NSNotification.Name.UITextViewTextDidBeginEditing, object: self)
defaultCenter.addObserver(textView, selector: #selector(handleTextViewTextDidChange), name: NSNotification.Name.UITextViewTextDidChange, object: self)
defaultCenter.addObserver(textView, selector: #selector(handleTextViewTextDidEnd), name: NSNotification.Name.UITextViewTextDidEndEditing, object: self)
}
}
extension Editor {
/// Updates the placeholderLabel text color.
fileprivate func updatePlaceholderLabelColor() {
tintColor = placeholderActiveColor
placeholderLabel.textColor = textView.isEditing ? placeholderActiveColor : placeholderNormalColor
}
}
extension Editor {
/**
Handler for when the keyboard will open.
- Parameter notification: A Notification.
*/
@objc
fileprivate func handleKeyboardWillShow(notification: Notification) {
guard isKeyboardHidden else {
return
}
isKeyboardHidden = false
guard let v = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue else {
return
}
delegate?.editor?(editor: self, willShowKeyboard: v)
}
/**
Handler for when the keyboard will close.
- Parameter notification: A Notification.
*/
@objc
fileprivate func handleKeyboardWillHide(notification: Notification) {
guard !isKeyboardHidden else {
return
}
isKeyboardHidden = true
guard let v = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
return
}
delegate?.editor?(editor: self, willHideKeyboard: v)
}
/// Notification handler for when text editing began.
@objc
fileprivate func handleTextViewTextDidBegin() {
}
/// Notification handler for when text changed.
@objc
fileprivate func handleTextViewTextDidChange() {
}
/// Notification handler for when text editing ended.
@objc
fileprivate func handleTextViewTextDidEnd() {
}
}
extension Editor: TextStorageDelegate {
@objc
open func textStorage(textStorage: TextStorage, willProcessEditing text: String, range: NSRange) {
delegate?.editor?(editor: self, willProcessEditing: textStorage, text: string, range: range)
}
@objc
open func textStorage(textStorage: TextStorage, didProcessEditing text: String, result: NSTextCheckingResult?, flags: NSRegularExpression.MatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) {
guard let range = result?.range else {
return
}
delegate?.editor?(editor: self, didProcessEditing: textStorage, text: string, range: range)
}
}
extension Editor: UITextViewDelegate {
@objc
open func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return delegate?.editor?(editor: self, shouldBeginEditing: textView) ?? true
}
@objc
open func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
return delegate?.editor?(editor: self, shouldEndEditing: textView) ?? true
}
@objc
open func textViewDidBeginEditing(_ textView: UITextView) {
delegate?.editor?(editor: self, didBeginEditing: textView)
}
@objc
open func textViewDidEndEditing(_ textView: UITextView) {
delegate?.editor?(editor: self, didEndEditing: textView)
}
@objc
open func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return delegate?.editor?(editor: self, textView: textView, shouldChangeTextIn: range, replacementText: text) ?? true
}
@objc
open func textViewDidChange(_ textView: UITextView) {
delegate?.editor?(editor: self, didChange: textView)
}
@objc
open func textViewDidChangeSelection(_ textView: UITextView) {
delegate?.editor?(editor: self, didChangeSelection: textView)
}
}
/*
@available(iOS, introduced: 8.0, deprecated: : 10.0)
extension Editor {
@objc
open func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
return delegate?.editor?(editor: self, textView: textView, shouldInteractWith: URL, in: characterRange) ?? true
}
@objc
open func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange) -> Bool {
return delegate?.editor?(editor: self, textView: textView, shouldInteractWith: textAttachment, in: characterRange) ?? true
}
}
@available(iOS 10.0, *)
extension Editor {
@objc
open func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return delegate?.editor?(editor: self, textView: textView, shouldInteractWith: URL, in: characterRange, interaction: interaction) ?? true
}
@objc
open func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return delegate?.editor?(editor: self, textView: textView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) ?? true
}
}
*/
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