Commit 6d0fde91 by whitepixelstudios Committed by GitHub

Merge pull request #1 from CosmicMind/master

Update to match CosmicMind version
parents 461dcd69 6bfd58cf
Pod::Spec.new do |s|
s.name = 'Material'
s.version = '2.5.2'
s.version = '2.6.3'
s.license = 'BSD-3-Clause'
s.summary = 'An animation and graphics framework for Material Design in Swift.'
s.homepage = 'http://materialswift.com'
......
......@@ -18,8 +18,6 @@
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, ); }; };
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, ); }; };
9617B0851DFCA8CF00410F8F /* EditorController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961DED4A1DCC546100F425B6 /* EditorController.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, ); }; };
9617B0881DFCA8CF00410F8F /* PhotoLibrary.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96717B161DBE6B1800DA84DB /* PhotoLibrary.swift */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -117,8 +115,6 @@
965E81211DD4D5C800D61E4B /* TextStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79D1CB40DC500C806FE /* TextStorage.swift */; };
965E81221DD4D5C800D61E4B /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB79E1CB40DC500C806FE /* TextView.swift */; };
965E81231DD4D7C800D61E4B /* BottomTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7591CB40DC500C806FE /* BottomTabBar.swift */; };
965E81241DD4D7C800D61E4B /* Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961DED451DCC40C500F425B6 /* Editor.swift */; };
965E81251DD4D7C800D61E4B /* EditorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961DED4A1DCC546100F425B6 /* EditorController.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, ); }; };
9697F7C01D8F2572004741EC /* Material+CALayer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96F1DC871D654FDF0025F925 /* Material+CALayer.swift */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -195,8 +191,6 @@
/* Begin PBXFileReference section */
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>"; };
961DED451DCC40C500F425B6 /* Editor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Editor.swift; sourceTree = "<group>"; };
961DED4A1DCC546100F425B6 /* EditorController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditorController.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>"; };
961EFC571D738FF600E84652 /* SnackbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnackbarController.swift; sourceTree = "<group>"; };
......@@ -311,15 +305,6 @@
name = TextField;
sourceTree = "<group>";
};
961DED441DCC40AC00F425B6 /* Editor */ = {
isa = PBXGroup;
children = (
961DED451DCC40C500F425B6 /* Editor.swift */,
961DED4A1DCC546100F425B6 /* EditorController.swift */,
);
name = Editor;
sourceTree = "<group>";
};
961E6BDD1DDA2A7E004E6C93 /* Application */ = {
isa = PBXGroup;
children = (
......@@ -377,12 +362,12 @@
name = Height;
sourceTree = "<group>";
};
962DDD071D6FBBB7001C307C /* Page */ = {
962DDD071D6FBBB7001C307C /* PageTabBar */ = {
isa = PBXGroup;
children = (
963FBF071D669D14008F8512 /* PageTabBarController.swift */,
);
name = Page;
name = PageTabBar;
sourceTree = "<group>";
};
962DDD081D6FBBD0001C307C /* BottomTabBar */ = {
......@@ -519,6 +504,7 @@
96BCB7571CB40DC500C806FE /* iOS */ = {
isa = PBXGroup;
children = (
96EF418E1E835E850012CA1C /* Animation */,
961E6BDD1DDA2A7E004E6C93 /* Application */,
96264BE41D833C8400576F37 /* Bar */,
962DDD081D6FBBD0001C307C /* BottomTabBar */,
......@@ -530,7 +516,6 @@
96328B9A1E05C135009A4C90 /* Data */,
96BCB80B1CB410CC00C806FE /* Device */,
96230AB61D6A51FD00AF47DC /* Divider */,
961DED441DCC40AC00F425B6 /* Editor */,
96BCB80A1CB410A100C806FE /* Extension */,
96BCB8071CB4101C00C806FE /* Font */,
9602F00C1DA1163000F3FB79 /* Grid */,
......@@ -542,7 +527,7 @@
96BCB8091CB4107700C806FE /* Motion */,
96BCB8011CB40F1700C806FE /* Navigation */,
961E6BEF1DDA4B04004E6C93 /* NavigationDrawer */,
962DDD071D6FBBB7001C307C /* Page */,
962DDD071D6FBBB7001C307C /* PageTabBar */,
96717B151DBE6B1800DA84DB /* Photos */,
96328B8F1E05B69A009A4C90 /* Reminders */,
9626CA951DAB5370003E2611 /* Root */,
......@@ -681,8 +666,6 @@
children = (
96BFC1671E61D9FD0075DE1F /* Material+Motion.swift */,
96BFC1691E61DAA10075DE1F /* Material+MotionAnimation.swift */,
96BCB7821CB40DC500C806FE /* PulseAnimation.swift */,
965532281E47E388005C2792 /* SpringAnimation.swift */,
);
name = Motion;
sourceTree = "<group>";
......@@ -750,6 +733,15 @@
path = Sources;
sourceTree = "<group>";
};
96EF418E1E835E850012CA1C /* Animation */ = {
isa = PBXGroup;
children = (
96BCB7821CB40DC500C806FE /* PulseAnimation.swift */,
965532281E47E388005C2792 /* SpringAnimation.swift */,
);
name = Animation;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
......@@ -821,8 +813,6 @@
9617B0811DFCA8CF00410F8F /* Capture.swift in Headers */,
9617B0821DFCA8CF00410F8F /* CapturePreview.swift in Headers */,
9617B0831DFCA8CF00410F8F /* CaptureController.swift in Headers */,
9617B0841DFCA8CF00410F8F /* Editor.swift in Headers */,
9617B0851DFCA8CF00410F8F /* EditorController.swift in Headers */,
9617B0861DFCA8CF00410F8F /* HeightPreset.swift in Headers */,
9617B0871DFCA8CF00410F8F /* PageTabBarController.swift in Headers */,
9617B0881DFCA8CF00410F8F /* PhotoLibrary.swift in Headers */,
......@@ -928,8 +918,6 @@
buildActionMask = 2147483647;
files = (
965E81231DD4D7C800D61E4B /* BottomTabBar.swift in Sources */,
965E81241DD4D7C800D61E4B /* Editor.swift in Sources */,
965E81251DD4D7C800D61E4B /* EditorController.swift in Sources */,
961E6BE21DDA2AF3004E6C93 /* Screen.swift in Sources */,
965E81261DD4D7C800D61E4B /* CharacterAttribute.swift in Sources */,
965E80FF1DD4D5C800D61E4B /* BottomNavigationController.swift in Sources */,
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0830"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......
......@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.5.2</string>
<string>2.6.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
......
......@@ -137,7 +137,6 @@ open class Button: UIButton, Pulseable, PulseableLayer {
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
tintColor = Color.blue.base
prepare()
}
......
......@@ -198,7 +198,7 @@ open class Card: PulseView {
}
container.height = h
height = h
bounds.size.height = h
}
/**
......
......@@ -117,7 +117,7 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values.
- 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 {
addAttribute(characterAttribute: k, value: v, range: range)
}
......@@ -139,7 +139,7 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values.
- 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 {
updateAttribute(characterAttribute: k, value: v, range: range)
}
......@@ -159,7 +159,7 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: An Array of CharacterAttributes.
- Parameter range: A NSRange.
*/
open func removeAttributes(characterAttributes: [CharacterAttribute] = [], range: NSRange) {
open func removeAttributes(characterAttributes: [CharacterAttribute], range: NSRange) {
for k in characterAttributes {
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
*/
}
open class Editor: View {
/// Will layout the view.
open var willLayout: Bool {
return 0 < width && 0 < height && nil != superview
}
/// 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 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: UITextView!
/// 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.
*/
public 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)
}
/**
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()
}
}
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 = UITextView(frame: .zero, textContainer: textContainer)
textView.delegate = self
addSubview(textView)
}
/// Prepares the regular expression for matching.
fileprivate func prepareRegularExpression() {
textStorage.expression = try? NSRegularExpression(pattern: pattern, options: [])
}
}
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
}
}
*/
/*
* 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
import AVFoundation
extension UIViewController {
/**
A convenience property that provides access to the EditorController.
This is the recommended method of accessing the EditorController
through child UIViewControllers.
*/
public var editorController: EditorController? {
var viewController: UIViewController? = self
while nil != viewController {
if viewController is EditorController {
return viewController as? EditorController
}
viewController = viewController?.parent
}
return nil
}
}
open class EditorController: ToolbarController {
/// A reference to the Editor instance.
@IBInspectable
open let editor = Editor()
/**
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()
view.backgroundColor = .white
prepareToolbar()
prepareEditor()
}
/// Prepares the toolbar.
private func prepareToolbar() {
toolbar.depthPreset = .none
}
/// Prepares editor.
private func prepareEditor() {
editor.delegate = self
}
}
extension EditorController: EditorDelegate {}
......@@ -97,7 +97,7 @@ private class FontLoader {
if !CTFontManagerRegisterGraphicsFont(font, &error) {
let errorDescription = CFErrorCopyDescription(error!.takeUnretainedValue())
let nsError = error!.takeUnretainedValue() as Any as! Error
NSException(name: .internalInconsistencyException, reason: errorDescription as? String, userInfo: [NSUnderlyingErrorKey: nsError as Any]).raise()
NSException(name: .internalInconsistencyException, reason: errorDescription as String?, userInfo: [NSUnderlyingErrorKey: nsError as Any]).raise()
}
}
}
......
......@@ -105,6 +105,6 @@ open class ImageCard: Card {
}
container.height = h
height = h
bounds.size.height = h
}
}
......@@ -44,7 +44,7 @@ extension Array where Element: Equatable {
}
guard -1 < start else {
fatalError("Range out of bounds for \(start) - \(end), should be 0 - \(count).")
fatalError("Range out of bounds for \(start) - \(end ?? 0), should be 0 - \(count).")
}
var diff = abs(e - start)
......
......@@ -173,7 +173,7 @@ extension UIViewController {
- Returns: An optional UIViewControllerAnimatedTransitioning.
*/
open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionEnabled ? Motion(isPresenting: true, isContainer: false) : nil
return isMotionEnabled ? PresentingMotion(isPresenting: true, isContainer: false) : nil
}
/**
......@@ -182,7 +182,7 @@ extension UIViewController {
- Returns: An optional UIViewControllerAnimatedTransitioning.
*/
open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return isMotionEnabled ? Motion() : nil
return isMotionEnabled ? DismissingMotion(isPresenting: true, isContainer: false) : nil
}
/**
......@@ -309,8 +309,7 @@ open class MotionPresentationController: UIPresentationController {
presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (context) in })
}
open override func presentationTransitionDidEnd(_ completed: Bool) {
}
open override func presentationTransitionDidEnd(_ completed: Bool) {}
open override func dismissalTransitionWillBegin() {
guard nil != containerView else {
......@@ -320,8 +319,7 @@ open class MotionPresentationController: UIPresentationController {
presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (context) in })
}
open override func dismissalTransitionDidEnd(_ completed: Bool) {
}
open override func dismissalTransitionDidEnd(_ completed: Bool) {}
open override var frameOfPresentedViewInContainerView: CGRect {
return containerView?.bounds ?? .zero
......@@ -338,9 +336,15 @@ public protocol MotionDelegate {
@objc
optional func motionDelayTransitionByTimeInterval(motion: Motion) -> TimeInterval
@objc
optional func motionWillBeginPresentation(presentationController: UIPresentationController)
@objc
optional func motionAnimateAlongsideTransition(presentationController: UIPresentationController)
}
open class Motion: NSObject {
open class MotionAnimator: NSObject {
/// A boolean indicating whether Motion is presenting a view controller.
open fileprivate(set) var isPresenting: Bool
......@@ -386,7 +390,7 @@ open class Motion: NSObject {
/// The view that is being transitioned to.
open var toView: UIView {
return transitionContext.view(forKey: .to)!
return toViewController.view
}
/// The subviews of the view being transitioned to.
......@@ -396,7 +400,7 @@ open class Motion: NSObject {
/// The view that is being transitioned from.
open var fromView: UIView {
return transitionContext.view(forKey: .from)!
return fromViewController.view
}
/// The subviews of the view being transitioned from.
......@@ -404,11 +408,6 @@ open class Motion: NSObject {
return Motion.subviews(of: fromView)
}
/// A time value to delay the transition animation by.
fileprivate var delayTransitionByTimeInterval: TimeInterval {
return fromViewController.motionDelegate?.motionDelayTransitionByTimeInterval?(motion: self) ?? 0
}
/// The default initializer.
public override init() {
isPresenting = false
......@@ -511,7 +510,7 @@ open class Motion: NSObject {
}
}
extension Motion: UIViewControllerAnimatedTransitioning {
extension MotionAnimator: UIViewControllerAnimatedTransitioning {
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
......@@ -519,21 +518,6 @@ extension Motion: UIViewControllerAnimatedTransitioning {
@objc(animateTransition:)
open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
self.transitionContext = transitionContext
fromViewController.motionDelegate?.motion?(motion: self, willTransition: fromView, toView: toView)
Motion.delay(delayTransitionByTimeInterval) { [weak self] in
guard let s = self else {
return
}
s.prepareContainerView()
s.prepareTransitionSnapshot()
s.prepareTransitionPairs()
s.prepareTransitionView()
s.prepareTransitionBackgroundView()
s.prepareToView()
s.prepareTransitionAnimation()
}
}
/**
......@@ -547,65 +531,7 @@ extension Motion: UIViewControllerAnimatedTransitioning {
}
}
extension Motion {
/// Prepares the containerView.
fileprivate func prepareContainerView() {
containerView = transitionContext.containerView
}
/// Prepares the transitionSnapshot.
fileprivate func prepareTransitionSnapshot() {
transitionSnapshot = fromView.transitionSnapshot(afterUpdates: true, shouldHide: false)
transitionSnapshot.frame = fromView.frame
containerView.insertSubview(transitionSnapshot, aboveSubview: fromView)
}
/// Prepares the transitionPairs.
fileprivate func prepareTransitionPairs() {
for from in fromSubviews {
for to in toSubviews {
guard to.motionIdentifier == from.motionIdentifier else {
continue
}
transitionPairs.append((from, to))
}
}
}
/// Prepares the transitionView.
fileprivate func prepareTransitionView() {
transitionView.frame = toView.bounds
transitionView.isUserInteractionEnabled = false
containerView.insertSubview(transitionView, belowSubview: transitionSnapshot)
}
/// Prepares the transitionBackgroundView.
fileprivate func prepareTransitionBackgroundView() {
transitionBackgroundView.backgroundColor = isPresenting ? .clear : fromView.backgroundColor ?? .clear
transitionBackgroundView.frame = transitionView.bounds
transitionView.addSubview(transitionBackgroundView)
}
/// Prepares the toView.
fileprivate func prepareToView() {
toView.isHidden = isPresenting
containerView.insertSubview(toView, belowSubview: transitionView)
toView.updateConstraints()
toView.setNeedsLayout()
toView.layoutIfNeeded()
}
/// Prepares the transition animation.
fileprivate func prepareTransitionAnimation() {
addTransitionAnimations()
addBackgroundAnimation()
cleanUpAnimation()
removeTransitionSnapshot()
}
}
extension Motion {
extension MotionAnimator {
/// Adds the available transition animations.
fileprivate func addTransitionAnimations() {
for (from, to) in transitionPairs {
......@@ -681,26 +607,7 @@ extension Motion {
}
}
extension Motion {
/**
Creates a CAAnimationGroup.
- Parameter animations: An Array of CAAnimation objects.
- Parameter timingFunction: An MotionAnimationTimingFunction value.
- Parameter duration: An animation duration time for the group.
- Returns: A CAAnimationGroup.
*/
open class func animate(group animations: [CAAnimation], timingFunction: MotionAnimationTimingFunction = .easeInEaseOut, duration: CFTimeInterval = 0.5) -> CAAnimationGroup {
let group = CAAnimationGroup()
group.fillMode = MotionAnimationFillModeToValue(mode: .forwards)
group.isRemovedOnCompletion = false
group.animations = animations
group.duration = duration
group.timingFunction = MotionAnimationTimingFunctionToValue(timingFunction: timingFunction)
return group
}
}
extension Motion {
extension MotionAnimator {
/**
Calculates the animation delay time based on the given Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
......@@ -759,6 +666,115 @@ extension Motion {
}
}
open class Motion: MotionAnimator {
/// A time value to delay the transition animation by.
fileprivate var delayTransitionByTimeInterval: TimeInterval {
return fromViewController.motionDelegate?.motionDelayTransitionByTimeInterval?(motion: self) ?? 0
}
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
*/
@objc(animateTransition:)
open override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
super.animateTransition(using: transitionContext)
fromViewController.motionDelegate?.motion?(motion: self, willTransition: fromView, toView: toView)
Motion.delay(delayTransitionByTimeInterval) { [weak self] in
guard let s = self else {
return
}
s.prepareContainerView()
s.prepareTransitionSnapshot()
s.prepareTransitionPairs()
s.prepareTransitionView()
s.prepareTransitionBackgroundView()
s.prepareToView()
s.prepareTransitionAnimation()
}
}
/// Prepares the toView.
fileprivate func prepareToView() {
toView.isHidden = isPresenting
containerView.insertSubview(toView, belowSubview: transitionView)
toView.frame = fromView.frame
toView.updateConstraints()
toView.setNeedsLayout()
toView.layoutIfNeeded()
}
}
extension Motion {
/// Prepares the containerView.
fileprivate func prepareContainerView() {
containerView = transitionContext.containerView
}
/// Prepares the transitionSnapshot.
fileprivate func prepareTransitionSnapshot() {
transitionSnapshot = fromView.transitionSnapshot(afterUpdates: true, shouldHide: false)
transitionSnapshot.frame = fromView.frame
containerView.addSubview(transitionSnapshot)
}
/// Prepares the transitionPairs.
fileprivate func prepareTransitionPairs() {
for from in fromSubviews {
for to in toSubviews {
guard to.motionIdentifier == from.motionIdentifier else {
continue
}
transitionPairs.append((from, to))
}
}
}
/// Prepares the transitionView.
fileprivate func prepareTransitionView() {
transitionView.frame = toView.bounds
transitionView.isUserInteractionEnabled = false
containerView.insertSubview(transitionView, belowSubview: transitionSnapshot)
}
/// Prepares the transitionBackgroundView.
fileprivate func prepareTransitionBackgroundView() {
transitionBackgroundView.backgroundColor = isPresenting ? .clear : fromView.backgroundColor ?? .clear
transitionBackgroundView.frame = transitionView.bounds
transitionView.addSubview(transitionBackgroundView)
}
/// Prepares the transition animation.
fileprivate func prepareTransitionAnimation() {
addTransitionAnimations()
addBackgroundAnimation()
cleanUpAnimation()
removeTransitionSnapshot()
}
}
extension Motion {
/**
Creates a CAAnimationGroup.
- Parameter animations: An Array of CAAnimation objects.
- Parameter timingFunction: An MotionAnimationTimingFunction value.
- Parameter duration: An animation duration time for the group.
- Returns: A CAAnimationGroup.
*/
open class func animate(group animations: [CAAnimation], timingFunction: MotionAnimationTimingFunction = .easeInEaseOut, duration: CFTimeInterval = 0.5) -> CAAnimationGroup {
let group = CAAnimationGroup()
group.fillMode = MotionAnimationFillModeToValue(mode: .forwards)
group.isRemovedOnCompletion = false
group.animations = animations
group.duration = duration
group.timingFunction = MotionAnimationTimingFunctionToValue(timingFunction: timingFunction)
return group
}
}
extension Motion {
/// Cleans up the animation transition.
fileprivate func cleanUpAnimation() {
......@@ -805,3 +821,17 @@ extension Motion {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
open class PresentingMotion: Motion {}
open class DismissingMotion: Motion {
/// Prepares the toView.
fileprivate override func prepareToView() {
toView.isHidden = true
toView.frame = fromView.frame
toView.updateConstraints()
toView.setNeedsLayout()
toView.layoutIfNeeded()
}
}
......@@ -374,10 +374,10 @@ extension UIView {
/// Computes the rotation of the view.
open var motionRotationAngle: CGFloat {
get {
return CGFloat(atan2f(Float(transform.b), Float(transform.a))) * 180 / CGFloat(M_PI)
return CGFloat(atan2f(Float(transform.b), Float(transform.a))) * 180 / CGFloat(Double.pi)
}
set(value) {
transform = CGAffineTransform(rotationAngle: CGFloat(M_PI) * value / 180)
transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi) * value / 180)
}
}
......@@ -506,7 +506,7 @@ extension Motion {
*/
public static func rotation(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotation)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * angle / 180))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation
}
......@@ -517,7 +517,7 @@ extension Motion {
*/
public static func rotationX(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationX)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * angle / 180))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation
}
......@@ -528,7 +528,7 @@ extension Motion {
*/
public static func rotationY(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationY)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * angle / 180))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation
}
......@@ -539,7 +539,7 @@ extension Motion {
*/
public static func rotationZ(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationZ)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * angle / 180))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation
}
......@@ -550,7 +550,7 @@ extension Motion {
*/
public static func spin(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotation)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * 2 * rotations))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
return animation
}
......@@ -561,7 +561,7 @@ extension Motion {
*/
public static func spinX(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationX)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * 2 * rotations))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
return animation
}
......@@ -572,7 +572,7 @@ extension Motion {
*/
public static func spinY(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationY)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * 2 * rotations))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
return animation
}
......@@ -583,7 +583,7 @@ extension Motion {
*/
public static func spinZ(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationZ)
animation.toValue = NSNumber(value: Double(CGFloat(M_PI) * 2 * rotations))
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
return animation
}
......
......@@ -39,7 +39,7 @@ extension UIFont {
- Returns a CGSize.
*/
open func stringSize(string: String, constrainedTo width: CGFloat) -> CGSize {
return string.boundingRect(with: CGSize(width: width, height: CGFloat(DBL_MAX)),
return string.boundingRect(with: CGSize(width: width, height: CGFloat(Double.greatestFiniteMagnitude)),
options: NSStringDrawingOptions.usesLineFragmentOrigin,
attributes: [NSFontAttributeName: self],
context: nil).size
......
......@@ -227,13 +227,13 @@ extension UIImage {
switch imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat(M_PI))
transform = transform.rotated(by: CGFloat(Double.pi))
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat(M_PI_2))
transform = transform.rotated(by: CGFloat(Double.pi / 2))
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: -CGFloat(M_PI_2))
transform = transform.rotated(by: -CGFloat(Double.pi / 2))
default:break
}
......@@ -296,8 +296,8 @@ extension UIImage {
let screenScale = Screen.scale
let imageRect = CGRect(origin: .zero, size: size)
let hasBlur = radius > CGFloat(FLT_EPSILON)
let hasSaturationChange = fabs(saturationDeltaFactor - 1.0) > CGFloat(FLT_EPSILON)
let hasBlur = radius > CGFloat(Float.ulpOfOne)
let hasSaturationChange = fabs(saturationDeltaFactor - 1.0) > CGFloat(Float.ulpOfOne)
if hasBlur || hasSaturationChange {
UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
......@@ -315,7 +315,7 @@ extension UIImage {
var outBuffer = createEffectBuffer(context: outContext)
if hasBlur {
let a = sqrt(2 * M_PI)
let a = sqrt(2 * .pi)
let b = CGFloat(a) / 4
let c = radius * screenScale
let d = c * 3.0 * b
......
......@@ -113,8 +113,18 @@ open class PageTabBarController: RootController {
/// Indicates that the tab has been pressed and animating.
open internal(set) var isTabSelectedAnimation = false
/// An internal reference to the selectedIndex.
fileprivate var internalSelectedIndex = 0
/// The currently selected UIViewController.
open internal(set) var selectedIndex = 0
open var selectedIndex: Int {
get {
return internalSelectedIndex
}
set(value) {
setInternalSelectedIndex(index: value, animated: true)
}
}
/// PageTabBar alignment setting.
open var pageTabBarAlignment = PageTabBarAlignment.bottom
......@@ -155,16 +165,15 @@ open class PageTabBarController: RootController {
isBounceEnabled = true
super.init(rootViewController: UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil))
viewControllers.append(rootViewController)
setViewControllers(viewControllers, direction: .forward, animated: true)
setInternalSelectedIndex(index: 0, animated: true)
prepare()
}
public init(viewControllers: [UIViewController], selectedIndex: Int = 0) {
public init(viewControllers: [UIViewController], selectedIndex index: Int = 0) {
isBounceEnabled = true
super.init(rootViewController: UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil))
self.selectedIndex = selectedIndex
self.viewControllers.append(contentsOf: viewControllers)
setViewControllers([self.viewControllers[selectedIndex]], direction: .forward, animated: true)
setInternalSelectedIndex(index: index, animated: true)
prepare()
}
......@@ -198,7 +207,6 @@ open class PageTabBarController: RootController {
*/
open func setViewControllers(_ viewControllers: [UIViewController], direction: UIPageViewControllerNavigationDirection, animated: Bool, completion: ((Bool) -> Void)? = nil) {
pageViewController?.setViewControllers(viewControllers, direction: direction, animated: animated, completion: completion)
prepare()
}
/**
......@@ -240,44 +248,65 @@ open class PageTabBarController: RootController {
button.addTarget(self, action: #selector(handlePageTabBarButton(button:)), for: .touchUpInside)
}
}
}
extension PageTabBarController {
/// Prepares the pageTabBar.
fileprivate func preparePageTabBar() {
pageTabBar.zPosition = 1000
pageTabBar.dividerColor = Color.grey.lighten3
view.addSubview(pageTabBar)
pageTabBar.select(at: internalSelectedIndex)
}
}
extension PageTabBarController {
/**
Handles the pageTabBarButton.
- Parameter button: A UIButton.
*/
@objc
internal func handlePageTabBarButton(button: UIButton) {
fileprivate func handlePageTabBarButton(button: UIButton) {
guard let index = pageTabBar.buttons.index(of: button) else {
return
}
guard index != selectedIndex else {
guard index != internalSelectedIndex else {
return
}
let direction: UIPageViewControllerNavigationDirection = index < selectedIndex ? .reverse : .forward
setInternalSelectedIndex(index: index, animated: true)
}
}
extension PageTabBarController {
/**
Internally sets the internalSelectedIndex value.
- Parameter index: Int.
- Parameter animated: Bool.
*/
fileprivate func setInternalSelectedIndex(index: Int, animated: Bool = false) {
guard animated else {
internalSelectedIndex = index
return
}
let direction: UIPageViewControllerNavigationDirection = index < internalSelectedIndex ? .reverse : .forward
isTabSelectedAnimation = true
selectedIndex = index
pageTabBar.select(at: selectedIndex)
internalSelectedIndex = index
pageTabBar.select(at: internalSelectedIndex)
setViewControllers([viewControllers[index]], direction: direction, animated: true) { [weak self] _ in
setViewControllers([viewControllers[internalSelectedIndex]], direction: direction, animated: true) { [weak self] _ in
guard let s = self else {
return
}
s.isTabSelectedAnimation = false
s.delegate?.pageTabBarController?(pageTabBarController: s, didTransitionTo: s.viewControllers[s.selectedIndex])
s.delegate?.pageTabBarController?(pageTabBarController: s, didTransitionTo: s.viewControllers[s.internalSelectedIndex])
}
}
/// Prepares the pageTabBar.
private func preparePageTabBar() {
pageTabBar.zPosition = 1000
pageTabBar.dividerColor = Color.grey.lighten3
view.addSubview(pageTabBar)
pageTabBar.select(at: selectedIndex)
}
}
extension PageTabBarController: UIPageViewControllerDelegate {
......@@ -290,8 +319,9 @@ extension PageTabBarController: UIPageViewControllerDelegate {
return
}
selectedIndex = index
pageTabBar.select(at: selectedIndex)
setInternalSelectedIndex(index: index)
pageTabBar.select(at: index)
if finished && completed {
delegate?.pageTabBarController?(pageTabBarController: self, didTransitionTo: v)
......
......@@ -79,6 +79,6 @@ open class PresenterCard: Card {
}
container.height = h
height = h
bounds.size.height = h
}
}
/*
* Copyright (C) 2015 - 2016, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* 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
......
......@@ -382,7 +382,8 @@ open class TextField: UITextField {
}
open override func becomeFirstResponder() -> Bool {
layoutSubviews()
setNeedsLayout()
layoutIfNeeded()
return super.becomeFirstResponder()
}
......@@ -398,6 +399,8 @@ open class TextField: UITextField {
borderStyle = .none
backgroundColor = nil
contentScaleFactor = Screen.scale
font = RobotoFont.regular(with: 16)
textColor = Color.darkText.primary
prepareDivider()
preparePlaceholderLabel()
......@@ -425,8 +428,8 @@ extension TextField {
/// Prepares the placeholderLabel.
fileprivate func preparePlaceholderLabel() {
font = RobotoFont.regular(with: 16)
placeholderNormalColor = Color.darkText.others
placeholderLabel.backgroundColor = .clear
addSubview(placeholderLabel)
}
......@@ -506,7 +509,7 @@ extension TextField {
/// Layout the detailLabel.
fileprivate func layoutDetailLabel() {
let c = dividerContentEdgeInsets
detailLabel.height = detailLabel.sizeThatFits(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)).height
detailLabel.height = detailLabel.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)).height
detailLabel.x = c.left
detailLabel.y = height + detailVerticalOffset
detailLabel.width = width - c.left - c.right
......
......@@ -31,10 +31,75 @@
import UIKit
@objc(TextViewDelegate)
public protocol TextViewDelegate : UITextViewDelegate {}
public protocol TextViewDelegate : UITextViewDelegate {
/**
A delegation method that is executed when the keyboard will open.
- Parameter textView: A TextView.
- Parameter willShowKeyboard value: A NSValue.
*/
@objc
optional func textView(textView: TextView, willShowKeyboard value: NSValue)
/**
A delegation method that is executed when the keyboard will close.
- Parameter textView: A TextView.
- Parameter willHideKeyboard value: A NSValue.
*/
@objc
optional func textView(textView: TextView, willHideKeyboard value: NSValue)
/**
A delegation method that is executed when the keyboard did open.
- Parameter textView: A TextView.
- Parameter didShowKeyboard value: A NSValue.
*/
@objc
optional func textView(textView: TextView, didShowKeyboard value: NSValue)
/**
A delegation method that is executed when the keyboard did close.
- Parameter textView: A TextView.
- Parameter didHideKeyboard value: A NSValue.
*/
@objc
optional func textView(textView: TextView, didHideKeyboard value: NSValue)
/**
A delegation method that is executed when text will be
processed during editing.
- Parameter textView: A TextView.
- Parameter willProcessEditing textStorage: A TextStorage.
- Parameter text: A String.
- Parameter range: A NSRange.
*/
@objc
optional func textView(textView: TextView, willProcessEditing textStorage: TextStorage, text: String, range: NSRange)
/**
A delegation method that is executed when text has been
processed after editing.
- Parameter textView: A TextView.
- Parameter didProcessEditing textStorage: A TextStorage.
- Parameter text: A String.
- Parameter range: A NSRange.
*/
@objc
optional func textView(textView: TextView, didProcessEditing textStorage: TextStorage, text: String, range: NSRange)
}
@objc(TextView)
open class TextView: UITextView {
/// A boolean indicating whether the text is empty.
open var isEmpty: Bool {
return 0 == text?.utf16.count
}
/// A boolean indicating whether the text is in edit mode.
open fileprivate(set) var isEditing = true
/// Is the keyboard hidden.
open fileprivate(set) var isKeyboardHidden = true
/// A property that accesses the backing layer's background
@IBInspectable
open override var backgroundColor: UIColor? {
......@@ -43,83 +108,115 @@ open class TextView: UITextView {
}
}
/**
The title UILabel that is displayed when there is text. The
titleLabel text value is updated with the placeholderLabel
text value before being displayed.
*/
/// The placeholderLabel font value.
@IBInspectable
open var titleLabel: UILabel? {
open override var font: UIFont? {
didSet {
prepareTitleLabel()
placeholderLabel.font = font
}
}
/// The color of the titleLabel text when the textView is not active.
/// The placeholderLabel text value.
@IBInspectable
open var titleLabelColor: UIColor? {
didSet {
titleLabel?.textColor = titleLabelColor
open var placeholder: String? {
get {
return placeholderLabel.text
}
set(value) {
placeholderLabel.text = value
}
}
/// The color of the titleLabel text when the textView is active.
/// The placeholder UILabel.
@IBInspectable
open var titleLabelActiveColor: UIColor?
open let placeholderLabel = UILabel()
/**
A property that sets the distance between the textView and
titleLabel.
*/
/// Placeholder normal text
@IBInspectable
open var titleLabelAnimationDistance: CGFloat = 8
/// Placeholder UILabel view.
open var placeholderLabel: UILabel? {
open var placeholderNormalColor = Color.darkText.others {
didSet {
preparePlaceholderLabel()
updatePlaceholderLabelColor()
}
}
/// An override to the text property.
/// Placeholder active text
@IBInspectable
open override var text: String! {
open var placeholderActiveColor = Color.blue.base {
didSet {
handleTextViewTextDidChange()
updatePlaceholderLabelColor()
}
}
/// An override to the attributedText property.
open override var attributedText: NSAttributedString! {
/// NSTextContainer EdgeInsets preset property.
open var textContainerInsetsPreset = EdgeInsetsPreset.none {
didSet {
handleTextViewTextDidChange()
textContainerInsets = EdgeInsetsPresetToValue(preset: textContainerInsetsPreset)
}
}
/// NSTextContainer EdgeInsets property.
open var textContainerInsets: EdgeInsets {
get {
return textContainerInset
}
set(value) {
textContainerInset = value
}
}
/**
Text container UIEdgeInset preset property. This updates the
textContainerInset property with a preset value.
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
*/
open var textContainerEdgeInsetsPreset: EdgeInsetsPreset = .none {
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
/// 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 {
textContainerInset = EdgeInsetsPresetToValue(preset: textContainerEdgeInsetsPreset)
prepareRegularExpression()
}
}
/// Text container UIEdgeInset property.
open override var textContainerInset: EdgeInsets {
/// A reference to the textView text.
open override var text: String! {
didSet {
reload()
setContentOffset(.zero, animated: true)
updatePlaceholderVisibility()
}
}
/**
An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance.
A convenience property that accesses the textStorage
string.
*/
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
open var string: String {
return textStorage.string
}
/// An Array of matches that match the pattern expression.
open var matches: [String] {
guard let v = (textStorage as? TextStorage)?.expression else {
return []
}
return v.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 set = Set<String>()
for x in matches {
set.insert(x)
}
return Array<String>(set)
}
/**
......@@ -142,168 +239,217 @@ open class TextView: UITextView {
self.init(frame: .zero, textContainer: textContainer)
}
/** Denitializer. This should never be called unless you know
/// A convenience initializer that constructs all aspects of the textView.
public convenience init() {
let textContainer = NSTextContainer(size: .zero)
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(textContainer)
let textStorage = TextStorage()
textStorage.addLayoutManager(layoutManager)
self.init(textContainer: textContainer)
textContainer.size = bounds.size
textStorage.delegate = self
}
/**
Denitializer. This should never be called unless you know
what you are doing.
*/
deinit {
removeNotificationHandlers()
NotificationCenter.default.removeObserver(self)
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutShape()
layoutShadowPath()
placeholderLabel?.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2
titleLabel?.frame.size.width = bounds.width
layoutPlaceholderLabel()
}
/// Reloads necessary components when the view has changed.
open func reload() {
if let p = placeholderLabel {
removeConstraints(constraints)
layout(p).edges(
top: textContainerInset.top,
left: textContainerInset.left + textContainer.lineFragmentPadding,
bottom: textContainerInset.bottom,
right: textContainerInset.right + textContainer.lineFragmentPadding)
/**
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 func prepare() {
contentScaleFactor = Screen.scale
textContainerInset = .zero
backgroundColor = nil
font = RobotoFont.regular(with: 16)
textColor = Color.darkText.primary
prepareNotificationHandlers()
prepareRegularExpression()
preparePlaceholderLabel()
}
}
extension TextView {
/// Prepares the Notification handlers.
fileprivate func prepareNotificationHandlers() {
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(self, selector: #selector(handleKeyboardDidShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
defaultCenter.addObserver(self, selector: #selector(handleKeyboardDidHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
defaultCenter.addObserver(self, selector: #selector(handleTextViewTextDidBegin), name: NSNotification.Name.UITextViewTextDidBeginEditing, object: self)
defaultCenter.addObserver(self, selector: #selector(handleTextViewTextDidChange), name: NSNotification.Name.UITextViewTextDidChange, object: self)
defaultCenter.addObserver(self, selector: #selector(handleTextViewTextDidEnd), name: NSNotification.Name.UITextViewTextDidEndEditing, object: self)
}
/// Notification handler for when text editing began.
@objc
fileprivate func handleTextViewTextDidBegin() {
titleLabel?.textColor = titleLabelActiveColor
/// Prepares the regular expression for matching.
fileprivate func prepareRegularExpression() {
(textStorage as? TextStorage)?.expression = try? NSRegularExpression(pattern: pattern, options: [])
}
/// Notification handler for when text changed.
@objc
fileprivate func handleTextViewTextDidChange() {
if let p = placeholderLabel {
p.isHidden = !(true == text?.isEmpty)
/// prepares the placeholderLabel property.
fileprivate func preparePlaceholderLabel() {
placeholderLabel.textColor = Color.darkText.others
placeholderLabel.textAlignment = textAlignment
placeholderLabel.numberOfLines = 0
placeholderLabel.backgroundColor = .clear
addSubview(placeholderLabel)
}
}
guard let t = text else {
hideTitleLabel()
return
extension TextView {
/// Updates the placeholderLabel text color.
fileprivate func updatePlaceholderLabelColor() {
tintColor = placeholderActiveColor
placeholderLabel.textColor = isEditing ? placeholderActiveColor : placeholderNormalColor
}
if 0 < t.utf16.count {
showTitleLabel()
} else {
hideTitleLabel()
/// Updates the placeholderLabel visibility.
fileprivate func updatePlaceholderVisibility() {
placeholderLabel.isHidden = !isEmpty
}
}
extension TextView {
/// Laysout the placeholder UILabel.
fileprivate func layoutPlaceholderLabel() {
placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2
let x = textContainerInset.left + textContainer.lineFragmentPadding
let y = textContainerInset.top
placeholderLabel.sizeToFit()
placeholderLabel.frame.origin.x = x
placeholderLabel.frame.origin.y = y
placeholderLabel.frame.size.width = textContainer.size.width - textContainerInset.right - textContainer.lineFragmentPadding
}
}
/// Notification handler for when text editing ended.
extension TextView {
/**
Handler for when the keyboard will open.
- Parameter notification: A Notification.
*/
@objc
fileprivate func handleTextViewTextDidEnd() {
guard let t = text else {
hideTitleLabel()
fileprivate func handleKeyboardWillShow(notification: Notification) {
guard isKeyboardHidden else {
return
}
if 0 < t.utf16.count {
showTitleLabel()
} else {
hideTitleLabel()
guard let v = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue else {
return
}
titleLabel?.textColor = titleLabelColor
(delegate as? TextViewDelegate)?.textView?(textView: self, willShowKeyboard: v)
}
/**
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.
Handler for when the keyboard did open.
- Parameter notification: A Notification.
*/
open func prepare() {
contentScaleFactor = Screen.scale
textContainerInset = .zero
backgroundColor = .white
clipsToBounds = false
removeNotificationHandlers()
prepareNotificationHandlers()
reload()
}
/// prepares the placeholderLabel property.
fileprivate func preparePlaceholderLabel() {
if let v: UILabel = placeholderLabel {
v.font = font
v.textAlignment = textAlignment
v.numberOfLines = 0
v.backgroundColor = .clear
addSubview(v)
reload()
handleTextViewTextDidChange()
}
@objc
fileprivate func handleKeyboardDidShow(notification: Notification) {
guard isKeyboardHidden else {
return
}
/// Prepares the titleLabel property.
fileprivate func prepareTitleLabel() {
if let v: UILabel = titleLabel {
v.isHidden = true
addSubview(v)
isKeyboardHidden = false
guard let t = text, 0 == t.utf16.count else {
v.alpha = 0
guard let v = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue else {
return
}
showTitleLabel()
(delegate as? TextViewDelegate)?.textView?(textView: self, didShowKeyboard: v)
}
/**
Handler for when the keyboard will close.
- Parameter notification: A Notification.
*/
@objc
fileprivate func handleKeyboardWillHide(notification: Notification) {
guard !isKeyboardHidden else {
return
}
/// Shows and animates the titleLabel property.
fileprivate func showTitleLabel() {
if let v: UILabel = titleLabel {
if v.isHidden {
if let s: String = placeholderLabel?.text {
v.text = s
guard let v = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
return
}
let h: CGFloat = ceil(v.font.lineHeight)
v.frame = CGRect(x: 0, y: -h, width: bounds.width, height: h)
v.isHidden = false
UIView.animate(withDuration: 0.25, animations: { [weak self] in
if let s: TextView = self {
v.alpha = 1
v.frame.origin.y = -v.frame.height - s.titleLabelAnimationDistance
(delegate as? TextViewDelegate)?.textView?(textView: self, willHideKeyboard: v)
}
})
/**
Handler for when the keyboard did close.
- Parameter notification: A Notification.
*/
@objc
fileprivate func handleKeyboardDidHide(notification: Notification) {
guard !isKeyboardHidden else {
return
}
isKeyboardHidden = true
guard let v = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
return
}
(delegate as? TextViewDelegate)?.textView?(textView: self, didHideKeyboard: v)
}
/// Hides and animates the titleLabel property.
fileprivate func hideTitleLabel() {
if let v: UILabel = titleLabel {
if !v.isHidden {
UIView.animate(withDuration: 0.25, animations: {
v.alpha = 0
v.frame.origin.y = -v.frame.height
}) { _ in
v.isHidden = true
/// Notification handler for when text editing began.
@objc
fileprivate func handleTextViewTextDidBegin() {
isEditing = true
}
/// Notification handler for when text changed.
@objc
fileprivate func handleTextViewTextDidChange() {
updatePlaceholderVisibility()
}
/// Notification handler for when text editing ended.
@objc
fileprivate func handleTextViewTextDidEnd() {
isEditing = false
updatePlaceholderVisibility()
}
}
extension TextView: TextStorageDelegate {
@objc
open func textStorage(textStorage: TextStorage, willProcessEditing text: String, range: NSRange) {
(delegate as? TextViewDelegate)?.textView?(textView: self, willProcessEditing: textStorage, text: string, range: range)
}
/// Prepares the Notification handlers.
fileprivate func prepareNotificationHandlers() {
let defaultCenter = NotificationCenter.default
defaultCenter.addObserver(self, selector: #selector(handleTextViewTextDidBegin), name: NSNotification.Name.UITextViewTextDidBeginEditing, object: self)
defaultCenter.addObserver(self, selector: #selector(handleTextViewTextDidChange), name: NSNotification.Name.UITextViewTextDidChange, object: self)
defaultCenter.addObserver(self, selector: #selector(handleTextViewTextDidEnd), name: NSNotification.Name.UITextViewTextDidEndEditing, object: self)
@objc
open func textStorage(textStorage: TextStorage, didProcessEditing text: String, result: NSTextCheckingResult?, flags: NSRegularExpression.MatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) {
guard let range = result?.range else {
return
}
/// Removes the Notification handlers.
fileprivate func removeNotificationHandlers() {
let defaultCenter = NotificationCenter.default
defaultCenter.removeObserver(self, name: NSNotification.Name.UITextViewTextDidBeginEditing, object: self)
defaultCenter.removeObserver(self, name: NSNotification.Name.UITextViewTextDidChange, object: self)
defaultCenter.removeObserver(self, name: NSNotification.Name.UITextViewTextDidEndEditing, object: self)
(delegate as? TextViewDelegate)?.textView?(textView: self, didProcessEditing: textStorage, text: string, range: range)
}
}
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