diff --git a/.travis.yml b/.travis.yml index 23f2426..36c1a85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ notifications: slack: lyokotech:7AmemNHORB2ShidELPFrYeUO email: false before_install: -- gem install cocoapods -v '1.1.0.rc.2' +- gem install cocoapods -v '1.1.1' script: - set -o pipefail && xcodebuild clean build -workspace Example/RichTextVC-iOS-Example.xcworkspace -scheme RichTextVC-iOS-Example -sdk iphonesimulator -destination 'OS=9.3,name=iPhone 5,platform=iOS Simulator' | xcpretty - pod lib lint diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 3ccc37f..2f4c82c 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -326,6 +326,11 @@ attributes = { LastSwiftUpdateCheck = 0730; LastUpgradeCheck = 0700; + TargetAttributes = { + 5BD45003813D61369ACCE237FE0CD7F8 = { + LastSwiftMigration = 0810; + }; + }; }; buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 3.2"; @@ -609,7 +614,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -676,7 +681,7 @@ PRODUCT_NAME = RichTextVC_iOS; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/Example/RichTextVC-iOS-Example.xcodeproj/project.pbxproj b/Example/RichTextVC-iOS-Example.xcodeproj/project.pbxproj index e65ff1f..8ef5b4f 100644 --- a/Example/RichTextVC-iOS-Example.xcodeproj/project.pbxproj +++ b/Example/RichTextVC-iOS-Example.xcodeproj/project.pbxproj @@ -184,11 +184,11 @@ TargetAttributes = { 5959A6811CCAA4CC005031E6 = { CreatedOnToolsVersion = 7.3; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; 5959A6951CCAA4CC005031E6 = { CreatedOnToolsVersion = 7.3; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; TestTargetID = 5959A6811CCAA4CC005031E6; }; }; @@ -472,7 +472,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.lyokotech.RichTextVC-iOS-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -487,7 +487,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.lyokotech.RichTextVC-iOS-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -500,7 +500,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.lyokotech.RichTextVC-iOS-ExampleUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; TEST_TARGET_NAME = "RichTextVC-iOS-Example"; }; name = Debug; @@ -515,7 +515,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.lyokotech.RichTextVC-iOS-ExampleUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; TEST_TARGET_NAME = "RichTextVC-iOS-Example"; }; name = Release; diff --git a/Example/RichTextVC-iOS-Example.xcodeproj/xcshareddata/xcschemes/RichTextVC-iOS-Example.xcscheme b/Example/RichTextVC-iOS-Example.xcodeproj/xcshareddata/xcschemes/RichTextVC-iOS-Example.xcscheme index d7c3f3b..743da90 100644 --- a/Example/RichTextVC-iOS-Example.xcodeproj/xcshareddata/xcschemes/RichTextVC-iOS-Example.xcscheme +++ b/Example/RichTextVC-iOS-Example.xcodeproj/xcshareddata/xcschemes/RichTextVC-iOS-Example.xcscheme @@ -28,6 +28,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - func applicationDidBecomeActive(application: UIApplication) { + func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - func applicationWillTerminate(application: UIApplication) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/RichTextVC-iOS.podspec b/RichTextVC-iOS.podspec index db13d1a..5b03688 100644 --- a/RichTextVC-iOS.podspec +++ b/RichTextVC-iOS.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "RichTextVC-iOS" - s.version = "1.5.1" + s.version = "2.0.0" s.summary = "A Rich Text ViewController for iOS." # This description is used to generate tags and improve search results. diff --git a/src/Classes/NSRange+Extras.swift b/src/Classes/NSRange+Extras.swift index ebd486e..d60564b 100644 --- a/src/Classes/NSRange+Extras.swift +++ b/src/Classes/NSRange+Extras.swift @@ -15,27 +15,27 @@ extension NSRange { return location + length } - func containsEntireRange(range: NSRange) -> Bool { + func containsEntireRange(_ range: NSRange) -> Bool { return containsBeginningOfRange(range) && containsEndOfRange(range) } - func containedInRange(range: NSRange) -> Bool { + func containedInRange(_ range: NSRange) -> Bool { return range.containsEntireRange(self) } - func containsEndOfRange(range: NSRange) -> Bool { + func containsEndOfRange(_ range: NSRange) -> Bool { return length > 0 && location <= range.endLocation && range.endLocation < endLocation } - func containsBeginningOfRange(range: NSRange) -> Bool { + func containsBeginningOfRange(_ range: NSRange) -> Bool { return length > 0 && location <= range.location && endLocation >= range.location } - func comesBeforeRange(range: NSRange) -> Bool { + func comesBeforeRange(_ range: NSRange) -> Bool { return endLocation <= range.location } - func comesAfterRange(range: NSRange) -> Bool { + func comesAfterRange(_ range: NSRange) -> Bool { return range.comesBeforeRange(self) } diff --git a/src/Classes/RichTextViewController.swift b/src/Classes/RichTextViewController.swift index 62ab601..622357c 100644 --- a/src/Classes/RichTextViewController.swift +++ b/src/Classes/RichTextViewController.swift @@ -8,55 +8,55 @@ import UIKit -public class RichTextViewController: UIViewController { +open class RichTextViewController: UIViewController { static let afterNumberCharacter = "." static let spaceAfterNumberCharacter = "\u{00A0}" - public static let bulletedLineStarter = "\u{2022}\u{00A0}" - public static var numberedListTrailer: String = { + open static let bulletedLineStarter = "\u{2022}\u{00A0}" + open static var numberedListTrailer: String = { return "\(afterNumberCharacter)\(spaceAfterNumberCharacter)" }() var previousSelection = NSRange() - public var textView = UITextView() + open var textView = UITextView() - public var regularFont: UIFont? - public var boldFont: UIFont? - public var italicFont: UIFont? - public var boldItalicFont: UIFont? + open var regularFont: UIFont? + open var boldFont: UIFont? + open var italicFont: UIFont? + open var boldItalicFont: UIFont? - private var disableBold = false - private var disableItalic = false + fileprivate var disableBold = false + fileprivate var disableItalic = false - private var defaultParagraphStyle: NSParagraphStyle = { + fileprivate var defaultParagraphStyle: NSParagraphStyle = { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.firstLineHeadIndent = 0 paragraphStyle.headIndent = 0 return paragraphStyle }() - private var defaultListParagraphStyle: NSParagraphStyle = { + fileprivate var defaultListParagraphStyle: NSParagraphStyle = { let listParagraphStyle = NSMutableParagraphStyle() listParagraphStyle.firstLineHeadIndent = 7 listParagraphStyle.headIndent = 7 return listParagraphStyle }() - private var defaultListAttributes: [String: AnyObject]? { + fileprivate var defaultListAttributes: [String: AnyObject]? { guard let regularFont = regularFont else { return nil } return [NSFontAttributeName: regularFont, NSParagraphStyleAttributeName: defaultListParagraphStyle] } deinit { - NSNotificationCenter.defaultCenter().removeObserver(self) + NotificationCenter.default.removeObserver(self) } - public override func viewDidLoad() { + open override func viewDidLoad() { super.viewDidLoad() - NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(textChanged), name: UITextViewTextDidChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(textChanged), name: NSNotification.Name.UITextViewTextDidChange, object: nil) } /// Replaces text in a range with text in parameter @@ -64,14 +64,14 @@ public class RichTextViewController: UIViewController { /// - parameter range: The range at which to replace the string. /// - parameter withText: The text that will be inserted. /// - parameter inTextView: The textView in which changes will occur. - private func replaceTextInRange(range: NSRange, withText replacementText: String, inTextView textView: UITextView) { - let substringLength = (textView.text as NSString).substringWithRange(range).length + fileprivate func replaceTextInRange(_ range: NSRange, withText replacementText: String, inTextView textView: UITextView) { + let substringLength = (textView.text as NSString).substring(with: range).length let lengthDifference = substringLength - replacementText.length let previousRange = textView.selectedRange - let attributes = textView.attributedText.attributesAtIndex(range.location, effectiveRange: nil) + let attributes = textView.attributedText.attributes(at: range.location, effectiveRange: nil) textView.textStorage.beginEditing() - textView.textStorage.replaceCharactersInRange(range, withAttributedString: NSAttributedString(string: replacementText, attributes: attributes)) + textView.textStorage.replaceCharacters(in: range, with: NSAttributedString(string: replacementText, attributes: attributes)) textView.textStorage.endEditing() let offset = lengthDifference - (previousRange.location - textView.selectedRange.location) @@ -84,8 +84,8 @@ public class RichTextViewController: UIViewController { /// /// - parameter range: The range of the text to remove. /// - parameter toTextView: The `UITextView` to remove the text from. - private func removeTextFromRange(range: NSRange, fromTextView textView: UITextView, applyDefault: Bool = true) { - let substringLength = (textView.text as NSString).substringWithRange(range).length + fileprivate func removeTextFromRange(_ range: NSRange, fromTextView textView: UITextView, applyDefault: Bool = true) { + let substringLength = (textView.text as NSString).substring(with: range).length let initialRange = textView.selectedRange if applyDefault { @@ -93,7 +93,7 @@ public class RichTextViewController: UIViewController { } textView.textStorage.beginEditing() - textView.textStorage.replaceCharactersInRange(range, withAttributedString: NSAttributedString(string: "")) + textView.textStorage.replaceCharacters(in: range, with: NSAttributedString(string: "")) textView.textStorage.endEditing() if range.comesBeforeRange(textView.selectedRange) { @@ -109,7 +109,7 @@ public class RichTextViewController: UIViewController { } /// Apply the default paragraph style (remove list paragraph style) from after the \n previous to the selected area to before the \n past the selected area - func applyDefaultParagraphStyleToSelectedRange(selectedRange: NSRange) { + func applyDefaultParagraphStyleToSelectedRange(_ selectedRange: NSRange) { let previousNewLineIndex = textView.text.previousIndexOfSubstring("\n", fromIndex: selectedRange.location) ?? 0 let nextNewLineIndex = textView.text.nextIndexOfSubstring("\n", fromIndex: selectedRange.location + selectedRange.length) ?? textView.text.length let fullChangeRange = NSRange(location: previousNewLineIndex, length: nextNewLineIndex - previousNewLineIndex) @@ -129,12 +129,12 @@ public class RichTextViewController: UIViewController { /// - parameter toTextView: The `UITextView` to add the text to. /// - parameter atIndex: The index to insert the text at. /// - parameter withAttributes: Optional. Attributes to apply to the added text. Will use attributes at the index otherwise. - private func addText(text: String, toTextView textView: UITextView, atIndex index: Int) { + fileprivate func addText(_ text: String, toTextView textView: UITextView, atIndex index: Int) { let previousTypingAttributes = textView.typingAttributes - let attributes = defaultListAttributes ?? (index < textView.text.length ? textView.attributedText.attributesAtIndex(index, effectiveRange: nil) : textView.typingAttributes) + let attributes = defaultListAttributes ?? (index < textView.text.length ? textView.attributedText.attributes(at: index, effectiveRange: nil) : textView.typingAttributes) textView.textStorage.beginEditing() - textView.textStorage.insertAttributedString(NSAttributedString(string: text, attributes: attributes), atIndex: index) + textView.textStorage.insert(NSAttributedString(string: text, attributes: attributes), at: index) textView.textStorage.endEditing() if textView.selectedRange.location <= index && index < textView.selectedRange.endLocation && textView.selectedRange.length > 0 { @@ -150,14 +150,14 @@ public class RichTextViewController: UIViewController { /// Toggles a numbered list on the current line if there is a zero-length selection; /// else removes all numbered lists in selection if they exist /// or adds them to each line if there are no numbered lists in selection - public func toggleNumberedList() { + open func toggleNumberedList() { if selectionContainsBulletedList(textView.selectedRange) { toggleBulletedList() } if textView.selectedRange.length == 0 { if selectionContainsNumberedList(textView.selectedRange) { - if let newLineIndex = textView.text.previousIndexOfSubstring("\n", fromIndex: textView.selectedRange.location), previousNumber = previousNumberOfNumberedList(textView.selectedRange) { + if let newLineIndex = textView.text.previousIndexOfSubstring("\n", fromIndex: textView.selectedRange.location), let previousNumber = previousNumberOfNumberedList(textView.selectedRange) { let range = NSRange(location: newLineIndex + 1, length: "\(previousNumber)\(RichTextViewController.numberedListTrailer)".length) removeTextFromRange(range, fromTextView: textView) } else { @@ -185,8 +185,7 @@ public class RichTextViewController: UIViewController { numbersInSelection = true var index = textView.selectedRange.location while index < textView.text.length { - guard let newRange = nextNumberedRangeFromIndex(index, inString: textView.text) - where newRange.location < textView.selectedRange.endLocation && + guard let newRange = nextNumberedRangeFromIndex(index, inString: textView.text), newRange.location < textView.selectedRange.endLocation && newRange.endLocation < textView.text.length && newRange.length > -1 else { @@ -212,7 +211,7 @@ public class RichTextViewController: UIViewController { var index = textView.selectedRange.location while index < textView.text.length { - guard let newLineIndex = textView.text.nextIndexOfSubstring("\n", fromIndex: index) where newLineIndex < textView.selectedRange.endLocation else { break } + guard let newLineIndex = textView.text.nextIndexOfSubstring("\n", fromIndex: index), newLineIndex < textView.selectedRange.endLocation else { break } addText("\(newNumber)\(RichTextViewController.numberedListTrailer)", toTextView: textView, atIndex: newLineIndex + 1) newNumber += 1 @@ -229,7 +228,7 @@ public class RichTextViewController: UIViewController { /// - parameter inString: The string to search in /// /// - returns: An `NSRange` describing the location of the previous number i.e. `"1. "` - private func previousNumberedRangeFromIndex(index: Int, inString string: String) -> NSRange? { + fileprivate func previousNumberedRangeFromIndex(_ index: Int, inString string: String) -> NSRange? { guard let numberedTrailerIndex = string.previousIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: index) else { return nil } var newLineIndex = string.previousIndexOfSubstring("\n", fromIndex: numberedTrailerIndex) ?? -1 @@ -246,7 +245,7 @@ public class RichTextViewController: UIViewController { /// - parameter inString: The string to search in /// /// - returns: An `NSRange` describing the location of the next number i.e. `"1. "` - private func nextNumberedRangeFromIndex(index: Int, inString string: String) -> NSRange? { + fileprivate func nextNumberedRangeFromIndex(_ index: Int, inString string: String) -> NSRange? { guard let numberedTrailerIndex = string.nextIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: index) else { return nil } var newLineIndex = string.previousIndexOfSubstring("\n", fromIndex: numberedTrailerIndex) ?? -1 @@ -263,14 +262,14 @@ public class RichTextViewController: UIViewController { /// - parameter selection: An `NSRange` to check /// /// - returns: True if selection contains at least 1 numbered list, false otherwise - public func selectionContainsNumberedList(range: NSRange) -> Bool { + open func selectionContainsNumberedList(_ range: NSRange) -> Bool { var containsNumberedList = false var selection = NSRange(location: range.location, length: range.length) if selection.length == 0 { if let previousIndex = textView.text.previousIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: selection.location) { let newLineIndex = textView.text.previousIndexOfSubstring("\n", fromIndex: selection.location) ?? 0 - if let comparisonIndex = textView.text.nextIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: newLineIndex) where previousIndex == comparisonIndex { + if let comparisonIndex = textView.text.nextIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: newLineIndex), previousIndex == comparisonIndex { containsNumberedList = true } } @@ -283,12 +282,12 @@ public class RichTextViewController: UIViewController { selection.length = selection.length < 2 && (selection.location + 2 < textView.text.length) ? 2 : selection.length - let substring = (textView.text as NSString).substringWithRange(selection) + let substring = (textView.text as NSString).substring(with: selection) - if substring.containsString(RichTextViewController.numberedListTrailer) { + if substring.contains(RichTextViewController.numberedListTrailer) { containsNumberedList = true } - } else if (textView.text as NSString).substringWithRange(selection).containsString(RichTextViewController.numberedListTrailer) { + } else if (textView.text as NSString).substring(with: selection).contains(RichTextViewController.numberedListTrailer) { containsNumberedList = true } } @@ -301,7 +300,7 @@ public class RichTextViewController: UIViewController { /// - parameter selection: The selection to check from /// /// - returns: Previous number if it exists in the current line or previous line, `nil` otherwise - private func previousNumberOfNumberedList(selection: NSRange) -> Int? { + fileprivate func previousNumberOfNumberedList(_ selection: NSRange) -> Int? { guard let previousNumberTrailIndex = textView.text.previousIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: selection.location) else { return nil } let indexOfPreviousNumberNewLine = textView.text.nextIndexOfSubstring("\n", fromIndex: previousNumberTrailIndex) ?? textView.text.length @@ -310,7 +309,7 @@ public class RichTextViewController: UIViewController { if selection.location <= indexOfNextNewLine { // Find the previous new line so we can get the entire number let indexOfNewLineBeforePreviousNumberTrailIndex = (textView.text.previousIndexOfSubstring("\n", fromIndex: previousNumberTrailIndex) ?? -1) + 1 - return Int((textView.text as NSString).substringWithRange(NSRange(location: indexOfNewLineBeforePreviousNumberTrailIndex, length: previousNumberTrailIndex - indexOfNewLineBeforePreviousNumberTrailIndex))) + return Int((textView.text as NSString).substring(with: NSRange(location: indexOfNewLineBeforePreviousNumberTrailIndex, length: previousNumberTrailIndex - indexOfNewLineBeforePreviousNumberTrailIndex))) } return nil @@ -321,7 +320,7 @@ public class RichTextViewController: UIViewController { /// - parameter range: The location to insert the number /// /// - returns: `true` if a number was added, `false` otherwise - private func addedListsIfActiveInRange(range: NSRange) -> Bool { + fileprivate func addedListsIfActiveInRange(_ range: NSRange) -> Bool { if selectionContainsNumberedList(range) { let previousNumber = previousNumberOfNumberedList(range) ?? 0 let previousNumberString = "\(previousNumber)\(RichTextViewController.numberedListTrailer)" @@ -329,7 +328,7 @@ public class RichTextViewController: UIViewController { var newNumber = previousNumber + 1 let newNumberString = "\n\(newNumber)\(RichTextViewController.numberedListTrailer)" - if textView.attributedText.attributedSubstringFromRange(previousRange).string == previousNumberString { + if textView.attributedText.attributedSubstring(from: previousRange).string == previousNumberString { removeTextFromRange(previousRange, fromTextView: textView) } else { addText(newNumberString, toTextView: textView, atIndex: range.location) @@ -354,8 +353,8 @@ public class RichTextViewController: UIViewController { let bulletedString = "\n" + RichTextViewController.bulletedLineStarter textView.textStorage.beginEditing() - if let subString = textView.attributedText?.attributedSubstringFromRange(previousRange).string where subString == RichTextViewController.bulletedLineStarter { - textView.textStorage.replaceCharactersInRange(previousRange, withAttributedString: NSAttributedString(string: "", attributes: textView.typingAttributes)) + if let subString = textView.attributedText?.attributedSubstring(from: previousRange).string, subString == RichTextViewController.bulletedLineStarter { + textView.textStorage.replaceCharacters(in: previousRange, with: NSAttributedString(string: "", attributes: textView.typingAttributes)) } else { addText(bulletedString, toTextView: textView, atIndex: range.location) } @@ -372,7 +371,7 @@ public class RichTextViewController: UIViewController { /// - parameter range: The range from which to remove the number /// /// - returns: true if a number was removed, false otherwise - private func removedListsIfActiveInRange(range: NSRange) -> Bool { + fileprivate func removedListsIfActiveInRange(_ range: NSRange) -> Bool { guard textView.selectedRange.location >= 2 else { return false } let previousNumber = previousNumberOfNumberedList(textView.selectedRange) ?? 0 @@ -384,15 +383,15 @@ public class RichTextViewController: UIViewController { if selectionContainsNumberedList(adjustedRange) { var removed = false - let subString = (textView.text as NSString).substringWithRange(previousNumberRange) + let subString = (textView.text as NSString).substring(with: previousNumberRange) if subString == previousNumberString { if previousNumber > 1 { // Not at the beginning of the list, so collapse the list - let space = NSCharacterSet.whitespaceAndNewlineCharacterSet() + let space = CharacterSet.whitespacesAndNewlines let text = textView.text as NSString while previousNumberRange.location > 0 { - if space.characterIsMember(text.characterAtIndex(previousNumberRange.location - 1)) { + if space.contains(UnicodeScalar(text.character(at: previousNumberRange.location - 1))!) { previousNumberRange.location = previousNumberRange.location - 1 previousNumberRange.length = previousNumberRange.length + 1 } else { @@ -422,10 +421,10 @@ public class RichTextViewController: UIViewController { } else if selectionContainsBulletedList(adjustedRange) { var removed = false - let subString = (textView.text as NSString).substringWithRange(previousBulletRange) + let subString = (textView.text as NSString).substring(with: previousBulletRange) if subString == RichTextViewController.bulletedLineStarter { textView.textStorage.beginEditing() - textView.textStorage.replaceCharactersInRange(previousBulletRange, withString: "") + textView.textStorage.replaceCharacters(in: previousBulletRange, with: "") textView.textStorage.endEditing() textView.selectedRange = NSRange(location: previousBulletRange.location, length: 0) removed = true @@ -438,13 +437,13 @@ public class RichTextViewController: UIViewController { } /// Moves the selection out of a number. Call this when a selection changes - private func moveSelectionIfInRangeOfNumberedList() { + fileprivate func moveSelectionIfInRangeOfNumberedList() { guard textView.text.length > 3 else { return } var range = NSRange(location: textView.selectedRange.location, length: textView.selectedRange.length) - func stringAtRange(range: NSRange) -> String { - return (textView.text as NSString).substringWithRange(range) + func stringAtRange(_ range: NSRange) -> String { + return (textView.text as NSString).substring(with: range) } if range.length == 0 { @@ -493,8 +492,7 @@ public class RichTextViewController: UIViewController { } } else if range.location > 0 && range.location < textView.text.length - 1 && stringAtRange(NSRange(location: range.location - 1, length: 1)) == "\n", let nextTrailerIndex = textView.text.nextIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: range.location), - nextLineIndex = textView.text.nextIndexOfSubstring("\n", fromIndex: range.location) - where nextTrailerIndex < nextLineIndex { + let nextLineIndex = textView.text.nextIndexOfSubstring("\n", fromIndex: range.location), nextTrailerIndex < nextLineIndex { if previousSelection.location < range.location { let oldLocation = range.location range.location = textView.text.nextIndexOfSubstring(RichTextViewController.numberedListTrailer, fromIndex: range.location) ?? textView.text.length - 2 @@ -545,13 +543,13 @@ public class RichTextViewController: UIViewController { // MARK: Font Adjustments - private func applyFontAttribute(font: UIFont) { + fileprivate func applyFontAttribute(_ font: UIFont) { guard let attributedString = textView.attributedText else { return } let attributedText = NSMutableAttributedString(attributedString: attributedString) attributedText.beginEditing() - attributedText.enumerateAttributesInRange(textView.selectedRange, options: []) { _, range, _ in + attributedText.enumerateAttributes(in: textView.selectedRange, options: []) { _, range, _ in attributedText.addAttribute(NSFontAttributeName, value: font, range: range) } attributedText.endEditing() @@ -559,15 +557,15 @@ public class RichTextViewController: UIViewController { textView.attributedText = attributedText } - private func removeFormattingFromListLeadsInRange(range: NSRange) { + fileprivate func removeFormattingFromListLeadsInRange(_ range: NSRange) { guard let regularFont = regularFont else { return } - guard range.length > 0, let listHeadRegex = try? NSRegularExpression(pattern: "^(([0-9]+\\.\\u00A0)|(\\u2022\\u00A0)).*$", options: .AnchorsMatchLines) else { + guard range.length > 0, let listHeadRegex = try? NSRegularExpression(pattern: "^(([0-9]+\\.\\u00A0)|(\\u2022\\u00A0)).*$", options: .anchorsMatchLines) else { print("Failed to remove formatting") return } - listHeadRegex.matchesInString(textView.text, options: [], range: range).forEach { match in - let matchedRange = match.rangeAtIndex(1) + listHeadRegex.matches(in: textView.text, options: [], range: range).forEach { match in + let matchedRange = match.rangeAt(1) self.textView.textStorage.beginEditing() self.textView.textStorage.setAttributes([NSFontAttributeName: regularFont], range: matchedRange) self.textView.textStorage.endEditing() @@ -576,19 +574,19 @@ public class RichTextViewController: UIViewController { // MARK: Bold Functions - public func selectionContainsBold(range: NSRange) -> Bool { + open func selectionContainsBold(_ range: NSRange) -> Bool { guard !disableBold else { return false } var font = range.length == 0 ? textView.typingAttributes[NSFontAttributeName] as? UIFont : nil - textView.attributedText.enumerateAttributesInRange(range, options: []) { dictionary, _, _ in + textView.attributedText.enumerateAttributes(in: range, options: []) { dictionary, _, _ in font = font ?? dictionary[NSFontAttributeName] as? UIFont } return font == boldFont || font == boldItalicFont } - public func toggleBold() { - guard let regularFont = regularFont, boldFont = boldFont, italicFont = italicFont, boldItalicFont = boldItalicFont else { return } + open func toggleBold() { + guard let regularFont = regularFont, let boldFont = boldFont, let italicFont = italicFont, let boldItalicFont = boldItalicFont else { return } let rangeLongerThanZero = textView.selectedRange.length > 0 let isItalic = selectionContainsItalic(textView.selectedRange) @@ -607,19 +605,19 @@ public class RichTextViewController: UIViewController { // MARK: Italic Functions - public func selectionContainsItalic(range: NSRange) -> Bool { + open func selectionContainsItalic(_ range: NSRange) -> Bool { guard !disableItalic else { return false } var font = range.length == 0 ? textView.typingAttributes[NSFontAttributeName] as? UIFont : nil - textView.attributedText.enumerateAttributesInRange(range, options: []) { dictionary, _, _ in + textView.attributedText.enumerateAttributes(in: range, options: []) { dictionary, _, _ in font = font ?? dictionary[NSFontAttributeName] as? UIFont } return font == italicFont || font == boldItalicFont } - public func toggleItalic() { - guard let regularFont = regularFont, boldFont = boldFont, italicFont = italicFont, boldItalicFont = boldItalicFont else { return } + open func toggleItalic() { + guard let regularFont = regularFont, let boldFont = boldFont, let italicFont = italicFont, let boldItalicFont = boldItalicFont else { return } let rangeLongerThanZero = textView.selectedRange.length > 0 let isItalic = selectionContainsItalic(textView.selectedRange) @@ -638,7 +636,7 @@ public class RichTextViewController: UIViewController { // MARK: Bulleted Lists - public func selectionContainsBulletedList(selection: NSRange) -> Bool { + open func selectionContainsBulletedList(_ selection: NSRange) -> Bool { var containsBulletedList = false if let previousIndex = textView.text.previousIndexOfSubstring(RichTextViewController.bulletedLineStarter, fromIndex: selection.location) { @@ -647,13 +645,13 @@ public class RichTextViewController: UIViewController { } if selection.length > 0 && !containsBulletedList { - containsBulletedList = (textView.text as NSString).substringWithRange(selection).containsString(RichTextViewController.bulletedLineStarter) + containsBulletedList = (textView.text as NSString).substring(with: selection).contains(RichTextViewController.bulletedLineStarter) } return containsBulletedList } - private func moveSelectionIfInRangeOfBulletedList() { + fileprivate func moveSelectionIfInRangeOfBulletedList() { guard textView.text.length > 1 && textView.selectedRange.location < textView.text.length else { return } var range = NSRange(location: textView.selectedRange.location, length: textView.selectedRange.length) @@ -664,7 +662,7 @@ public class RichTextViewController: UIViewController { } } var loops = 0 - var testString = (textView.text as NSString).substringWithRange(range) + var testString = (textView.text as NSString).substring(with: range) while loops < 2 { if testString == RichTextViewController.bulletedLineStarter { range.location += 2 @@ -678,12 +676,12 @@ public class RichTextViewController: UIViewController { } else { break } - testString = (textView.text as NSString).substringWithRange(range) + testString = (textView.text as NSString).substring(with: range) loops += 1 } } - public func toggleBulletedList() { + open func toggleBulletedList() { if selectionContainsNumberedList(textView.selectedRange) { toggleNumberedList() } @@ -735,7 +733,7 @@ public class RichTextViewController: UIViewController { extension RichTextViewController: UITextViewDelegate { - public func textViewDidChangeSelection(textView: UITextView) { + public func textViewDidChangeSelection(_ textView: UITextView) { disableBold = false disableItalic = false @@ -744,7 +742,7 @@ extension RichTextViewController: UITextViewDelegate { previousSelection = textView.selectedRange } - public func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool { + public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { var changed = false switch text { @@ -759,7 +757,7 @@ extension RichTextViewController: UITextViewDelegate { return !changed } - func textChanged(notification: NSNotification) { + func textChanged(_ notification: Notification) { guard notification.object as? UITextView == textView else { return } if textView.selectedRange.endLocation == textView.text.length { diff --git a/src/Classes/String+Searching.swift b/src/Classes/String+Searching.swift index 71b4017..d9f9d89 100644 --- a/src/Classes/String+Searching.swift +++ b/src/Classes/String+Searching.swift @@ -16,14 +16,14 @@ extension String { /// - parameter fromIndex: The index to start the search from. The search will move backwards from this index. /// /// - returns: Index of the searchString passed in. Nil if `fromIndex` is invalid, or if string is not found. - func previousIndexOfSubstring(searchString: String, fromIndex: Int) -> Int? { + func previousIndexOfSubstring(_ searchString: String, fromIndex: Int) -> Int? { if fromIndex < 0 { return nil } - let substring = substringToIndex(characters.startIndex.advancedBy(fromIndex)) - if let range = substring.rangeOfString(searchString, options: .BackwardsSearch) { - return substring.startIndex.distanceTo(range.startIndex) + let substring = self.substring(to: characters.index(characters.startIndex, offsetBy: fromIndex)) + if let range = substring.range(of: searchString, options: .backwards) { + return substring.characters.distance(from: substring.startIndex, to: range.lowerBound) } return nil @@ -35,14 +35,14 @@ extension String { /// - parameter fromIndex: The index to start the search from. The search will move forwards from this index. /// /// - returns: Index of the searchString passed in. Nil if `fromIndex` is invalid, or if string is not found. - func nextIndexOfSubstring(searchString: String, fromIndex: Int) -> Int? { + func nextIndexOfSubstring(_ searchString: String, fromIndex: Int) -> Int? { if fromIndex < 0 { return nil } - let substring = substringFromIndex(characters.startIndex.advancedBy(fromIndex)) - if let range = substring.rangeOfString(searchString) { - return substring.startIndex.distanceTo(range.startIndex) + fromIndex + let substring = self.substring(from: characters.index(characters.startIndex, offsetBy: fromIndex)) + if let range = substring.range(of: searchString) { + return substring.characters.distance(from: substring.startIndex, to: range.lowerBound) + fromIndex } return nil @@ -52,4 +52,4 @@ extension String { return (self as NSString).length } -} \ No newline at end of file +}