diff --git a/.github/example.gif b/.github/example.gif
new file mode 100644
index 0000000..39486ab
Binary files /dev/null and b/.github/example.gif differ
diff --git a/.gitignore b/.gitignore
index e43b0f9..0b90dbe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,6 @@
.DS_Store
+/.build
+/Packages
+/*.xcodeproj
+xcuserdata/
+.swiftpm
diff --git a/AutocompleteField.podspec b/AutocompleteField.podspec
index a1c6e28..a0cd429 100644
--- a/AutocompleteField.podspec
+++ b/AutocompleteField.podspec
@@ -1,27 +1,13 @@
Pod::Spec.new do |s|
-
- # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
- s.name = "AutocompleteField"
- s.version = "1.1"
- s.summary = "Add word completion to your UITextFields."
-
- s.homepage = "http://iosdev.io/autocompletefield"
- s.screenshots = "https://camo.githubusercontent.com/9a525a5623198884f545d43c7aa0df857488c2c6/68747470733a2f2f646c2e64726f70626f7875736572636f6e74656e742e636f6d2f752f3935383439392f707265766965772e6769663f646c3d30"
-
- # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
- s.license = "MIT"
-
- # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
- s.author = { "Filip Stefansson" => "filip.stefansson@gmail.com" }
-
- # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
- s.platform = :ios
- s.ios.deployment_target = "8.0"
-
- # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
- s.source = { :git => "https://github.com/filipstefansson/AutocompleteField.git", :tag => "1.1" }
-
- # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
- s.source_files = "AutocompleteField/**/*.swift"
-
+ s.name = "AutocompleteField"
+ s.version = "2.0"
+ s.summary = "Subclass of UITextField that shows inline suggestions while typing."
+ s.homepage = "https://github.com/filipstefansson/AutocompleteField"
+ s.screenshots = "https://raw.githubusercontent.com/filipstefansson/AutocompleteField/master/.github/example.gif?raw=1"
+ s.license = { :type => "MIT", :file => "LICENSE" }
+ s.author = { "Filip Stefansson" => "filip.stefansson@gmail.com" }
+ s.platform = :ios
+ s.ios.deployment_target = "10.0"
+ s.source = { :git => "https://github.com/filipstefansson/AutocompleteField.git", :tag => "2.0" }
+ s.source_files = "Sources/**/*.swift"
end
diff --git a/AutocompleteField.xcworkspace/contents.xcworkspacedata b/AutocompleteField.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..5577df0
--- /dev/null
+++ b/AutocompleteField.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/AutocompleteField.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/AutocompleteField.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/AutocompleteField.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/AutocompleteField/AutocompleteField.swift b/AutocompleteField/AutocompleteField.swift
deleted file mode 100644
index a13446e..0000000
--- a/AutocompleteField/AutocompleteField.swift
+++ /dev/null
@@ -1,268 +0,0 @@
-//
-// AutocompleteField.swift
-// Example
-//
-// Created by Filip Stefansson on 05/11/15.
-// Copyright © 2015 Filip Stefansson. All rights reserved.
-//
-
-import Foundation
-import UIKit
-
-
-public enum AutocompleteType {
- case Word
- case Sentence
-}
-
-
-@IBDesignable public class AutocompleteField: UITextField
-{
- // MARK: - public properties
-
- // left/right padding
- @IBInspectable public var padding : CGFloat = 0
-
- // the color of the suggestion. Matches the default placeholder color
- @IBInspectable public var completionColor : UIColor = UIColor(white: 0, alpha: 0.22)
-
- // Array of suggestions
- public var suggestions : [String] = [""]
-
- // The current suggestion shown. Can also be used to force a suggestion
- public var suggestion : String? {
- didSet {
- if let val = suggestion {
- setLabelContent(val)
- }
- }
- }
-
- // Move the suggestion label up or down. Sometimes there's a small difference, and this can be used to fix it.
- public var pixelCorrection : CGFloat = 0
-
- // Update the suggestion when the text is changed using 'field.text'
- override public var text : String? {
- didSet {
- if let text = text {
- self.setLabelContent(text)
- }
- }
- }
-
- // The type of autocomplete that should be used
- public var autocompleteType : AutocompleteType = .Word
-
-
- // MARK: - private properties
-
- // the suggestion label
- private var label = UILabel()
-
-
- // MARK: - init functions
-
- override public init(frame: CGRect)
- {
- super.init(frame: frame)
-
- createNotification()
- setupLabel()
- }
-
- required public init?(coder aDecoder: NSCoder)
- {
- super.init(coder: aDecoder)
-
- createNotification()
- setupLabel()
- }
-
- /**
- Create an instance of a AutocompleteField.
- - parameter
- frame: The fields frame
- suggestion: Array of autocomplete strings
- */
- public init(frame: CGRect, suggestions: [String])
- {
- super.init(frame: frame)
-
- self.suggestions = suggestions
- createNotification()
- setupLabel()
- }
-
-
- // ovverride to set frame of the suggestion label whenever the textfield frame changes.
- public override func layoutSubviews()
- {
- self.label.frame = CGRectMake(self.padding, self.pixelCorrection, self.frame.width - (self.padding * 2), self.frame.height)
- super.layoutSubviews()
- }
-
- // MARK: - public methods
- public func currentSuggestion() -> NSString?
- {
- return self.suggestion
- }
-
-
- // MARK: - private methods
-
- /**
- Create a notification whenever the text of the field changes.
- */
- private func createNotification()
- {
- NSNotificationCenter.defaultCenter().addObserver(
- self,
- selector: "textChanged:",
- name: UITextFieldTextDidChangeNotification,
- object: self)
- }
-
- /**
- Sets up the suggestion label with the same font styling and alignment as the textfield.
- */
- private func setupLabel()
- {
- setLabelContent()
-
- self.label.lineBreakMode = .ByClipping
-
- // If the textfield has one of the default styles, we need to create some padding
- // otherwise there will be a offset in x-led.
- switch self.borderStyle
- {
- case .RoundedRect, .Bezel, .Line:
- self.padding = 8
- break;
- default:
- break;
- }
-
- self.addSubview(self.label)
- }
-
-
- /**
- Set content of the suggestion label.
- - parameter text: Suggestion text
- */
- private func setLabelContent(var text : String = "")
- {
- // label string
- if(text.characters.count < 1) {
- label.attributedText = nil
- return
- }
-
- // only return first word if in word mode
- if(self.autocompleteType == .Word)
- {
- let words = self.text!.componentsSeparatedByString(" ")
- let suggestionWords = text.componentsSeparatedByString(" ")
- var string : String = ""
- for(var i = 0; i < words.count; i++)
- {
- string = string.stringByAppendingString(suggestionWords[i]) + " "
- }
- text = string
- }
-
- // create an attributed string instead of the regular one.
- // In this way we can hide the letters in the suggestion that the user has already written.
- let attributedString = NSMutableAttributedString(
- string: text,
- attributes: [
- NSFontAttributeName:UIFont(
- name: self.font!.fontName,
- size: self.font!.pointSize
- )!,
- NSForegroundColorAttributeName: self.completionColor
- ]
- )
-
- // Hide the letters that are under the fields text.
- // If the suggestion is abcdefgh and the user has written abcd
- // we want to hide those letters from the suggestion.
- if let inputText = self.text
- {
- attributedString.addAttribute(NSForegroundColorAttributeName,
- value: UIColor.clearColor(),
- range: NSRange(location:0, length:inputText.characters.count)
- )
- }
-
- label.attributedText = attributedString
- label.textAlignment = self.textAlignment
- }
-
- /**
- Scans through the suggestions array and finds a suggestion that
- matches the searchTerm.
- - parameter searchTerm: What to search for
- - returns A string or nil
- */
- private func suggestionToShow(searchTerm : String) -> String
- {
- var returnString = ""
- for suggestion in self.suggestions
- {
- // Search the suggestion array. User lowercase on both to get a match.
- // Also, if the match is exact we move on.
- if( (suggestion != searchTerm) &&
- suggestion.lowercaseString.hasPrefix(searchTerm.lowercaseString))
- {
- var suggestionToReturn = searchTerm
- suggestionToReturn = suggestionToReturn + suggestion.substringWithRange(Range(start: suggestion.startIndex.advancedBy(searchTerm.characters.count), end: suggestion.endIndex))
-
- returnString = suggestionToReturn
- break
- }
- }
- self.suggestion = returnString
- return returnString
- }
-
-
- // MARK: - Events
-
- /**
- Triggered whenever the field text changes.
- - parameter notification: The NSNotifcation attached to the event
- */
- func textChanged(notification: NSNotification)
- {
- if let text = self.text
- {
- let suggestion = suggestionToShow(text)
- setLabelContent(suggestion)
- }
- }
-
- // ovverride to set padding
- public override func textRectForBounds(bounds: CGRect) -> CGRect
- {
- return CGRectMake(bounds.origin.x + self.padding, bounds.origin.y,
- bounds.size.width - (self.padding * 2), bounds.size.height);
- }
-
- // ovverride to set padding
- public override func editingRectForBounds(bounds: CGRect) -> CGRect
- {
- return self.textRectForBounds(bounds)
- }
-
- // ovverride to set padding on placeholder
- public override func placeholderRectForBounds(bounds: CGRect) -> CGRect
- {
- return self.textRectForBounds(bounds)
- }
-
- // remove observer on deinit
- deinit {
- NSNotificationCenter.defaultCenter().removeObserver(self)
- }
-}
diff --git a/Example/.gitignore b/Example/.gitignore
deleted file mode 100644
index f393909..0000000
--- a/Example/.gitignore
+++ /dev/null
@@ -1,31 +0,0 @@
-# Created by https://www.gitignore.io/api/swift
-
-### Swift ###
-# Xcode
-#
-# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
-
-## Build generated
-build/
-DerivedData
-
-## Various settings
-*.pbxuser
-!default.pbxuser
-*.mode1v3
-!default.mode1v3
-*.mode2v3
-!default.mode2v3
-*.perspectivev3
-!default.perspectivev3
-xcuserdata
-
-## Other
-*.xccheckout
-*.moved-aside
-*.xcuserstate
-*.xcscmblueprint
-
-## Obj-C/Swift specific
-*.hmap
-*.ipa
diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj
index 98bb2e9..2502dc8 100644
--- a/Example/Example.xcodeproj/project.pbxproj
+++ b/Example/Example.xcodeproj/project.pbxproj
@@ -3,31 +3,33 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
- 5A0A45431BEBD6CD000BFC25 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A0A45421BEBD6CD000BFC25 /* AppDelegate.swift */; };
- 5A0A45451BEBD6CD000BFC25 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A0A45441BEBD6CD000BFC25 /* ViewController.swift */; };
- 5A0A45481BEBD6CD000BFC25 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5A0A45461BEBD6CD000BFC25 /* Main.storyboard */; };
- 5A0A454A1BEBD6CD000BFC25 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A0A45491BEBD6CD000BFC25 /* Assets.xcassets */; };
- 5A0A454D1BEBD6CD000BFC25 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5A0A454B1BEBD6CD000BFC25 /* LaunchScreen.storyboard */; };
- 5A0A45551BEBD6FF000BFC25 /* AutocompleteField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A0A45541BEBD6FF000BFC25 /* AutocompleteField.swift */; };
+ 5AC976A325359B850031B69E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC976A225359B850031B69E /* AppDelegate.swift */; };
+ 5AC976A525359B850031B69E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC976A425359B850031B69E /* SceneDelegate.swift */; };
+ 5AC976A725359B850031B69E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC976A625359B850031B69E /* ViewController.swift */; };
+ 5AC976AA25359B850031B69E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5AC976A825359B850031B69E /* Main.storyboard */; };
+ 5AC976AC25359B860031B69E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5AC976AB25359B860031B69E /* Assets.xcassets */; };
+ 5AC976AF25359B860031B69E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5AC976AD25359B860031B69E /* LaunchScreen.storyboard */; };
+ 5AC976BB25359BEA0031B69E /* AutocompleteField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC976B925359BCD0031B69E /* AutocompleteField.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
- 5A0A453F1BEBD6CD000BFC25 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
- 5A0A45421BEBD6CD000BFC25 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
- 5A0A45441BEBD6CD000BFC25 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
- 5A0A45471BEBD6CD000BFC25 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
- 5A0A45491BEBD6CD000BFC25 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
- 5A0A454C1BEBD6CD000BFC25 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
- 5A0A454E1BEBD6CD000BFC25 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- 5A0A45541BEBD6FF000BFC25 /* AutocompleteField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutocompleteField.swift; path = ../../AutocompleteField/AutocompleteField.swift; sourceTree = ""; };
+ 5AC9769F25359B850031B69E /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5AC976A225359B850031B69E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 5AC976A425359B850031B69E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
+ 5AC976A625359B850031B69E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
+ 5AC976A925359B850031B69E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 5AC976AB25359B860031B69E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 5AC976AE25359B860031B69E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 5AC976B025359B860031B69E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 5AC976B925359BCD0031B69E /* AutocompleteField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteField.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
- 5A0A453C1BEBD6CD000BFC25 /* Frameworks */ = {
+ 5AC9769C25359B850031B69E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -37,46 +39,56 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 5A0A45361BEBD6CD000BFC25 = {
+ 5AC9769625359B850031B69E = {
isa = PBXGroup;
children = (
- 5A0A45411BEBD6CD000BFC25 /* Example */,
- 5A0A45401BEBD6CD000BFC25 /* Products */,
+ 5AC976B825359BCD0031B69E /* Sources */,
+ 5AC976A125359B850031B69E /* Example */,
+ 5AC976A025359B850031B69E /* Products */,
);
sourceTree = "";
};
- 5A0A45401BEBD6CD000BFC25 /* Products */ = {
+ 5AC976A025359B850031B69E /* Products */ = {
isa = PBXGroup;
children = (
- 5A0A453F1BEBD6CD000BFC25 /* Example.app */,
+ 5AC9769F25359B850031B69E /* Example.app */,
);
name = Products;
sourceTree = "";
};
- 5A0A45411BEBD6CD000BFC25 /* Example */ = {
+ 5AC976A125359B850031B69E /* Example */ = {
isa = PBXGroup;
children = (
- 5A0A45541BEBD6FF000BFC25 /* AutocompleteField.swift */,
- 5A0A45421BEBD6CD000BFC25 /* AppDelegate.swift */,
- 5A0A45441BEBD6CD000BFC25 /* ViewController.swift */,
- 5A0A45461BEBD6CD000BFC25 /* Main.storyboard */,
- 5A0A45491BEBD6CD000BFC25 /* Assets.xcassets */,
- 5A0A454B1BEBD6CD000BFC25 /* LaunchScreen.storyboard */,
- 5A0A454E1BEBD6CD000BFC25 /* Info.plist */,
+ 5AC976A225359B850031B69E /* AppDelegate.swift */,
+ 5AC976A425359B850031B69E /* SceneDelegate.swift */,
+ 5AC976A625359B850031B69E /* ViewController.swift */,
+ 5AC976A825359B850031B69E /* Main.storyboard */,
+ 5AC976AB25359B860031B69E /* Assets.xcassets */,
+ 5AC976AD25359B860031B69E /* LaunchScreen.storyboard */,
+ 5AC976B025359B860031B69E /* Info.plist */,
);
path = Example;
sourceTree = "";
};
+ 5AC976B825359BCD0031B69E /* Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 5AC976B925359BCD0031B69E /* AutocompleteField.swift */,
+ );
+ name = Sources;
+ path = ../Sources;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
- 5A0A453E1BEBD6CD000BFC25 /* Example */ = {
+ 5AC9769E25359B850031B69E /* Example */ = {
isa = PBXNativeTarget;
- buildConfigurationList = 5A0A45511BEBD6CD000BFC25 /* Build configuration list for PBXNativeTarget "Example" */;
+ buildConfigurationList = 5AC976B325359B860031B69E /* Build configuration list for PBXNativeTarget "Example" */;
buildPhases = (
- 5A0A453B1BEBD6CD000BFC25 /* Sources */,
- 5A0A453C1BEBD6CD000BFC25 /* Frameworks */,
- 5A0A453D1BEBD6CD000BFC25 /* Resources */,
+ 5AC9769B25359B850031B69E /* Sources */,
+ 5AC9769C25359B850031B69E /* Frameworks */,
+ 5AC9769D25359B850031B69E /* Resources */,
);
buildRules = (
);
@@ -84,81 +96,81 @@
);
name = Example;
productName = Example;
- productReference = 5A0A453F1BEBD6CD000BFC25 /* Example.app */;
+ productReference = 5AC9769F25359B850031B69E /* Example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
- 5A0A45371BEBD6CD000BFC25 /* Project object */ = {
+ 5AC9769725359B850031B69E /* Project object */ = {
isa = PBXProject;
attributes = {
- LastSwiftUpdateCheck = 0710;
- LastUpgradeCheck = 0710;
- ORGANIZATIONNAME = "Filip Stefansson";
+ LastSwiftUpdateCheck = 1200;
+ LastUpgradeCheck = 1200;
TargetAttributes = {
- 5A0A453E1BEBD6CD000BFC25 = {
- CreatedOnToolsVersion = 7.1;
+ 5AC9769E25359B850031B69E = {
+ CreatedOnToolsVersion = 12.0.1;
};
};
};
- buildConfigurationList = 5A0A453A1BEBD6CD000BFC25 /* Build configuration list for PBXProject "Example" */;
- compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ buildConfigurationList = 5AC9769A25359B850031B69E /* Build configuration list for PBXProject "Example" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
- mainGroup = 5A0A45361BEBD6CD000BFC25;
- productRefGroup = 5A0A45401BEBD6CD000BFC25 /* Products */;
+ mainGroup = 5AC9769625359B850031B69E;
+ productRefGroup = 5AC976A025359B850031B69E /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
- 5A0A453E1BEBD6CD000BFC25 /* Example */,
+ 5AC9769E25359B850031B69E /* Example */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
- 5A0A453D1BEBD6CD000BFC25 /* Resources */ = {
+ 5AC9769D25359B850031B69E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 5A0A454D1BEBD6CD000BFC25 /* LaunchScreen.storyboard in Resources */,
- 5A0A454A1BEBD6CD000BFC25 /* Assets.xcassets in Resources */,
- 5A0A45481BEBD6CD000BFC25 /* Main.storyboard in Resources */,
+ 5AC976AF25359B860031B69E /* LaunchScreen.storyboard in Resources */,
+ 5AC976AC25359B860031B69E /* Assets.xcassets in Resources */,
+ 5AC976AA25359B850031B69E /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
- 5A0A453B1BEBD6CD000BFC25 /* Sources */ = {
+ 5AC9769B25359B850031B69E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 5A0A45551BEBD6FF000BFC25 /* AutocompleteField.swift in Sources */,
- 5A0A45451BEBD6CD000BFC25 /* ViewController.swift in Sources */,
- 5A0A45431BEBD6CD000BFC25 /* AppDelegate.swift in Sources */,
+ 5AC976BB25359BEA0031B69E /* AutocompleteField.swift in Sources */,
+ 5AC976A725359B850031B69E /* ViewController.swift in Sources */,
+ 5AC976A325359B850031B69E /* AppDelegate.swift in Sources */,
+ 5AC976A525359B850031B69E /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
- 5A0A45461BEBD6CD000BFC25 /* Main.storyboard */ = {
+ 5AC976A825359B850031B69E /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
- 5A0A45471BEBD6CD000BFC25 /* Base */,
+ 5AC976A925359B850031B69E /* Base */,
);
name = Main.storyboard;
sourceTree = "";
};
- 5A0A454B1BEBD6CD000BFC25 /* LaunchScreen.storyboard */ = {
+ 5AC976AD25359B860031B69E /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
- 5A0A454C1BEBD6CD000BFC25 /* Base */,
+ 5AC976AE25359B860031B69E /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "";
@@ -166,29 +178,44 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
- 5A0A454F1BEBD6CD000BFC25 /* Debug */ = {
+ 5AC976B125359B860031B69E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
- GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -202,38 +229,54 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.1;
- MTL_ENABLE_DEBUG_INFO = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
- 5A0A45501BEBD6CD000BFC25 /* Release */ = {
+ 5AC976B225359B860031B69E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
- GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -241,57 +284,74 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.1;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
- TARGETED_DEVICE_FAMILY = "1,2";
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
- 5A0A45521BEBD6CD000BFC25 /* Debug */ = {
+ 5AC976B425359B860031B69E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = Example/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.pixbymedia.Example;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.pixby.autocomplete-field-example.Example";
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 4.2;
+ TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
- 5A0A45531BEBD6CD000BFC25 /* Release */ = {
+ 5AC976B525359B860031B69E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = Example/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.pixbymedia.Example;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.pixby.autocomplete-field-example.Example";
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 4.2;
+ TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
- 5A0A453A1BEBD6CD000BFC25 /* Build configuration list for PBXProject "Example" */ = {
+ 5AC9769A25359B850031B69E /* Build configuration list for PBXProject "Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- 5A0A454F1BEBD6CD000BFC25 /* Debug */,
- 5A0A45501BEBD6CD000BFC25 /* Release */,
+ 5AC976B125359B860031B69E /* Debug */,
+ 5AC976B225359B860031B69E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- 5A0A45511BEBD6CD000BFC25 /* Build configuration list for PBXNativeTarget "Example" */ = {
+ 5AC976B325359B860031B69E /* Build configuration list for PBXNativeTarget "Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- 5A0A45521BEBD6CD000BFC25 /* Debug */,
- 5A0A45531BEBD6CD000BFC25 /* Release */,
+ 5AC976B425359B860031B69E /* Debug */,
+ 5AC976B525359B860031B69E /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
- rootObject = 5A0A45371BEBD6CD000BFC25 /* Project object */;
+ rootObject = 5AC9769725359B850031B69E /* Project object */;
}
diff --git a/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 6d2a51b..0000000
--- a/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/Example/Example/AppDelegate.swift b/Example/Example/AppDelegate.swift
index ef496c0..6fed0cd 100644
--- a/Example/Example/AppDelegate.swift
+++ b/Example/Example/AppDelegate.swift
@@ -2,43 +2,33 @@
// AppDelegate.swift
// Example
//
-// Created by Filip Stefansson on 05/11/15.
-// Copyright © 2015 Filip Stefansson. All rights reserved.
+// Created by Filip Stefansson on 2020-10-13.
//
import UIKit
-@UIApplicationMain
+@main
class AppDelegate: UIResponder, UIApplicationDelegate {
- var window: UIWindow?
- func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
- 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) {
- // 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) {
- // 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.
- }
+ // MARK: UISceneSession Lifecycle
- 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 application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+ // Called when a new scene session is being created.
+ // Use this method to select a configuration to create the new scene with.
+ return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
- func applicationWillTerminate(application: UIApplication) {
- // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+ func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
+ // Called when the user discards a scene session.
+ // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
+ // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
diff --git a/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json
index 36d2c80..9221b9b 100644
--- a/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -2,67 +2,97 @@
"images" : [
{
"idiom" : "iphone",
- "size" : "29x29",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "20x20"
},
{
"idiom" : "iphone",
- "size" : "29x29",
- "scale" : "3x"
+ "scale" : "3x",
+ "size" : "20x20"
},
{
"idiom" : "iphone",
- "size" : "40x40",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "29x29"
},
{
"idiom" : "iphone",
- "size" : "40x40",
- "scale" : "3x"
+ "scale" : "3x",
+ "size" : "29x29"
},
{
"idiom" : "iphone",
- "size" : "60x60",
- "scale" : "2x"
+ "scale" : "2x",
+ "size" : "40x40"
},
{
"idiom" : "iphone",
- "size" : "60x60",
- "scale" : "3x"
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
},
{
"idiom" : "ipad",
- "size" : "29x29",
- "scale" : "1x"
+ "scale" : "2x",
+ "size" : "20x20"
},
{
"idiom" : "ipad",
- "size" : "29x29",
- "scale" : "2x"
+ "scale" : "1x",
+ "size" : "29x29"
},
{
"idiom" : "ipad",
- "size" : "40x40",
- "scale" : "1x"
+ "scale" : "2x",
+ "size" : "29x29"
},
{
"idiom" : "ipad",
- "size" : "40x40",
- "scale" : "2x"
+ "scale" : "1x",
+ "size" : "40x40"
},
{
"idiom" : "ipad",
- "size" : "76x76",
- "scale" : "1x"
+ "scale" : "2x",
+ "size" : "40x40"
},
{
"idiom" : "ipad",
- "size" : "76x76",
- "scale" : "2x"
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
-}
\ No newline at end of file
+}
diff --git a/Example/Example/Assets.xcassets/Contents.json b/Example/Example/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Example/Example/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Example/Example/Base.lproj/LaunchScreen.storyboard b/Example/Example/Base.lproj/LaunchScreen.storyboard
index 7f23d4f..865e932 100644
--- a/Example/Example/Base.lproj/LaunchScreen.storyboard
+++ b/Example/Example/Base.lproj/LaunchScreen.storyboard
@@ -1,23 +1,20 @@
-
+
-
-
+
+
+
-
-
-
-
-
+
-
-
+
+
diff --git a/Example/Example/Base.lproj/Main.storyboard b/Example/Example/Base.lproj/Main.storyboard
index 7c7b469..25a7638 100644
--- a/Example/Example/Base.lproj/Main.storyboard
+++ b/Example/Example/Base.lproj/Main.storyboard
@@ -1,183 +1,24 @@
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
diff --git a/Example/Example/Info.plist b/Example/Example/Info.plist
index 40c6215..5b531f7 100644
--- a/Example/Example/Info.plist
+++ b/Example/Example/Info.plist
@@ -3,7 +3,7 @@
CFBundleDevelopmentRegion
- en
+ $(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -13,15 +13,34 @@
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
- APPL
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
1.0
- CFBundleSignature
- ????
CFBundleVersion
1
LSRequiresIPhoneOS
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneConfigurationName
+ Default Configuration
+ UISceneDelegateClassName
+ $(PRODUCT_MODULE_NAME).SceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
diff --git a/Example/Example/SceneDelegate.swift b/Example/Example/SceneDelegate.swift
new file mode 100644
index 0000000..a18325c
--- /dev/null
+++ b/Example/Example/SceneDelegate.swift
@@ -0,0 +1,52 @@
+//
+// SceneDelegate.swift
+// Example
+//
+// Created by Filip Stefansson on 2020-10-13.
+//
+
+import UIKit
+
+class SceneDelegate: UIResponder, UIWindowSceneDelegate {
+
+ var window: UIWindow?
+
+
+ func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
+ // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
+ // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
+ // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
+ guard let _ = (scene as? UIWindowScene) else { return }
+ }
+
+ func sceneDidDisconnect(_ scene: UIScene) {
+ // Called as the scene is being released by the system.
+ // This occurs shortly after the scene enters the background, or when its session is discarded.
+ // Release any resources associated with this scene that can be re-created the next time the scene connects.
+ // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
+ }
+
+ func sceneDidBecomeActive(_ scene: UIScene) {
+ // Called when the scene has moved from an inactive state to an active state.
+ // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
+ }
+
+ func sceneWillResignActive(_ scene: UIScene) {
+ // Called when the scene will move from an active state to an inactive state.
+ // This may occur due to temporary interruptions (ex. an incoming phone call).
+ }
+
+ func sceneWillEnterForeground(_ scene: UIScene) {
+ // Called as the scene transitions from the background to the foreground.
+ // Use this method to undo the changes made on entering the background.
+ }
+
+ func sceneDidEnterBackground(_ scene: UIScene) {
+ // Called as the scene transitions from the foreground to the background.
+ // Use this method to save data, release shared resources, and store enough scene-specific state information
+ // to restore the scene back to its current state.
+ }
+
+
+}
+
diff --git a/Example/Example/ViewController.swift b/Example/Example/ViewController.swift
index 374e37f..d045dfb 100644
--- a/Example/Example/ViewController.swift
+++ b/Example/Example/ViewController.swift
@@ -1,107 +1,82 @@
//
// ViewController.swift
-// Example
+// BasicExample
//
-// Created by Filip Stefansson on 05/11/15.
-// Copyright © 2015 Filip Stefansson. All rights reserved.
+// Created by Filip Stefansson on 2020-10-09.
//
import UIKit
-class ViewController: UITableViewController, UITextFieldDelegate {
-
- // the fields
- @IBOutlet weak var basicField: AutocompleteField!
- @IBOutlet weak var customField: AutocompleteField!
- @IBOutlet weak var emailField: AutocompleteField!
-
- // the content view for code example
- @IBOutlet weak var codeView: UIView!
-
+class ViewController: UIViewController {
override func viewDidLoad() {
-
super.viewDidLoad()
- // set delegates
- basicField.delegate = self
- customField.delegate = self
- emailField.delegate = self
-
- // autocomplete suggestions
- let suggestions = ["Abraham Lincoln", "John Doe", "John Smith", "Awesome"]
- basicField.suggestions = suggestions
- customField.suggestions = suggestions
-
- // change event for email field
- NSNotificationCenter.defaultCenter().addObserver(
- self,
- selector: "emailChanged:",
- name: UITextFieldTextDidChangeNotification,
- object: emailField)
-
-
- // add code example
- let textField = AutocompleteField(frame: CGRectMake(0, 0, self.codeView.frame.size.width, self.codeView.frame.size.height), suggestions: ["abraham"])
-
- textField.padding = 8
- textField.placeholder = "Try typing 'A'"
- textField.translatesAutoresizingMaskIntoConstraints = false
- self.codeView.addSubview(textField)
-
- // auto layout
- let vertivalConstraint = textField.centerYAnchor.constraintEqualToAnchor(self.codeView.centerYAnchor)
- let leadingConstraint = textField.leadingAnchor.constraintEqualToAnchor(self.codeView.leadingAnchor, constant: 10)
- let trailingConstraint = textField.trailingAnchor.constraintEqualToAnchor(self.codeView.trailingAnchor, constant: -10)
- let heightConstraint = textField.heightAnchor.constraintEqualToAnchor(nil, constant: 37)
- NSLayoutConstraint.activateConstraints([leadingConstraint, vertivalConstraint, trailingConstraint, heightConstraint])
+ self.addNameFieldExample()
+ self.addEmailFieldExample()
}
- func emailChanged(notification: NSNotification)
- {
- if let text = self.emailField.text
- {
- if text.characters.count > 0
- {
- // Check if the user has started writing the suffix yet.
- var suffix = "@gmail.com"
- let regex = try! NSRegularExpression(pattern: "^[A-Z0-9._%+-]+@",options: [.CaseInsensitive])
- let result = regex.firstMatchInString(text, options:[], range: NSMakeRange(0, text.characters.count))
-
- if let range = result?.range
- {
- // If user has started writing the suffix, only suggest what's left of it
- let regSuffix = text.substringWithRange(Range(start: text.startIndex.advancedBy(range.length - 1), end: text.endIndex))
- suffix = suffix.stringByReplacingOccurrencesOfString(regSuffix, withString: "")
- }
-
- self.emailField.suggestion = text + suffix
- }
- else
- {
- // Remove suggestion if the field is empty
- self.emailField.suggestion = ""
- }
- }
-
+ /// Example showing a field with a custom font and borderStyle
+ private func addNameFieldExample() {
+ // add AutocompleteField to main view
+ let textfield = AutocompleteField(frame: CGRect(x: 20, y: 100, width: self.view.bounds.width - 40, height: 50))
+
+ textfield.font = UIFont(name: "American Typewriter", size: 20)
+ textfield.placeholder = "Name"
+ textfield.borderStyle = .roundedRect
+ textfield.autocorrectionType = .no
+
+ // add suggestions to textfield
+ textfield.suggestions = [
+ "George Washington",
+ "Thomas Jefferson",
+ "John Adams",
+ "Theodore Roosevelt",
+ "John F. Kennedy",
+ "George W. Bush",
+ ]
+
+ // set delegate and add to view
+ textfield.delegate = self
+ self.view.addSubview(textfield)
}
- // MARK: - UITextField delegate
- func textFieldShouldReturn(textField: UITextField) -> Bool
- {
- // set field text to the suggestion text on return
- if let field = textField as? AutocompleteField
- {
- field.text = field.suggestion
- }
- return true
- }
+ /// Example showing how the delimiter option can be used for emails
+ private func addEmailFieldExample() {
+ // add AutocompleteField to main view
+ let textfield = AutocompleteField(frame: CGRect(x: 20, y: 160, width: self.view.bounds.width - 40, height: 50))
+
+ textfield.font = UIFont(name: "American Typewriter", size: 20)
+ textfield.placeholder = "Email"
+ textfield.borderStyle = .roundedRect
+ textfield.autocorrectionType = .no
+ textfield.keyboardType = .emailAddress
+ textfield.autocapitalizationType = .none
+
+ // adda delimiter so we only show suggestions after the @ character
+ textfield.delimiter = "@"
- override func didReceiveMemoryWarning()
- {
- super.didReceiveMemoryWarning()
- // Dispose of any resources that can be recreated.
+ // add email providers as suggestions
+ textfield.suggestions = [
+ "gmail.com",
+ "icloud.com",
+ "outlook.com",
+ ]
+
+ // set delegate and add to view
+ textfield.delegate = self
+ self.view.addSubview(textfield)
}
+}
+
+extension ViewController: UITextFieldDelegate {
+ func textFieldShouldReturn(_ tf: UITextField) -> Bool {
+ // fill out the rest when user hits return
+ let textField = tf as! AutocompleteField
+ textField.text = textField.suggestion
+ textField.resignFirstResponder()
+ return true
+ }
}
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..68f576d
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,18 @@
+// swift-tools-version:5.3
+import PackageDescription
+
+let package = Package(
+ name: "AutocompleteField",
+ platforms: [.iOS(.v10)],
+ products: [
+ .library(
+ name: "AutocompleteField",
+ targets: ["AutocompleteField"]),
+ ],
+ targets: [
+ .target(
+ name: "AutocompleteField",
+ path: "Sources"
+ ),
+ ]
+)
diff --git a/README.md b/README.md
index 72d1ff2..fb592cb 100644
--- a/README.md
+++ b/README.md
@@ -1,59 +1,116 @@
-![AutocompleteField](https://dl.dropboxusercontent.com/u/958499/autocomplete.png?dl=0)
+# AutocompleteField
-AutocompleteField let's you add word completion to your UITextFields.
+[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/AutocompleteField.svg)](https://img.shields.io/cocoapods/v/AutocompleteField.svg)
+[![Platform](https://img.shields.io/cocoapods/p/AutocompleteField.svg?style=flat)](https://AutocompleteField.github.io/AutocompleteField)
-> Autocomplete, or word completion, is a feature in which an application predicts the rest of a word a user is typing.
+Subclass of `UITextField` that shows inline suggestions while typing.
-## How it works
+- Plug and play replacement for `UITextField`.
+- [Delimiter support](#delimiter). Perfect when autocompleting email addresses.
+- Two suggestion modes (word and sentence, see [API](#api) below).
+- Works with custom fonts, borders etc.
+- Super lightweight and zero dependencies.
-![AutocompleteField](https://dl.dropboxusercontent.com/u/958499/preview.gif?dl=0)
+---
-## Installation
+![AutocompleteField](/.github/example.gif?raw=true)
-### Manual installation
+# Requirements
-Import `AutocompleteField.swift` into your project.
+- iOS 10.0+
+- Swift 4.2+
-### CocoaPods
+# Installation
+## CocoaPods
+
+Add the following to your `Podfile`:
+
+```ruby
+target 'MyApp' do
+ pod 'AutocompleteField', '~> 2.0'
+end
```
-platform :ios, '8.0'
-pod "AutocompleteField", "~> 1.1"
-```
-## Usage
+## Swift Package Manager
+
+- Select **File > Swift Packages > Add Package Dependency**.
+- Enter `https://github.com/filipstefansson/AutocompleteField.git` in the **Choose Package Repository** dialog.
+
+See [Apple docs](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app) for more information.
+
+## Manually
-The easiest way is to add a `UITextField` in your Storyboard, and then giving it the `AutocompleteField` subclass. You can use the property editor to change both the padding and the completion color of the textfield.
+- Copy `/Sources/AutocompleteField.swift` to your project. There are no other dependencies.
-If you want to add a field using code, there's a custom init method you can use:
+# Usage
+
+You use this textfield in the same way as the regular `UITextField`, through Storyboards or programmatically.
+
+## Basic
```swift
import AutocompleteField
-let textField = AutocompleteField(frame: CGRectMake(10, 10, 200, 40), suggestions: ["Abraham", "George", "Franklin"])
-view.addSubview(textField)
+...
+
+let textfield = AutocompleteField(frame: CGRect(x: 20, y: 20, width: 200, height: 40))
+textfield.placeholder = "Name"
+
+textfield.suggestions = [
+ "George Washington",
+ "Thomas Jefferson",
+ "John Adams",
+ "Theodore Roosevelt",
+ "John F. Kennedy",
+ "George W. Bush",
+]
+
+self.view.addSubview(textfield)
```
-## Customization
+## Delimiter
+
+The delimiter can be used to only suggest an autocompletion after a specific character is found in the string. In this example we look for the `@` character, and then provide suggestions for email providers.
+
+```swift
+import AutocompleteField
+
+...
+
+// email textfield autocompleting email providers
+let textfield = AutocompleteField(frame: CGRect(x: 20, y: 20, width: 200, height: 40))
+textfield.placeholder = "Email"
+textfield.keyboardType = .emailAddress
+
+textfield.suggestions = [
+ "gmail.com",
+ "icloud.com",
+ "outlook.com",
+]
+
+// add the delimiter
+textfield.delimiter = "@"
+
+self.view.addSubview(textfield)
+```
-AutocompleteField is a subclass of UITextField, so you can modify it in the same way you normally would, without any restrictions. The new properties you can set are:
+# API
-Property | Type | Description
---------- | ---- | -----------
-`padding` | `CGFloat` | Left/right padding.
-`completionColor` | `UIColor` | The color of the suggestion. Defaults to the default placeholder color.
-`suggestion` | `String` | The current suggestion shown. Can be used to force a suggestion.
-`suggestions` | `[String]` | Array of suggestions.
-`autocompleteType` | `AutocompleteType` | The type of autocomplete that should be used. .Word will only hint the the next word in the suggestion and .Sentence will show all words.
-`pixelCorrection` | `CGFloat` | Move the suggestion label up or down. Sometimes there's a small difference, and this can be used to fix it.
+| Property | Type | Description |
+| ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `suggestionColor` | `UIColor` | The color of the suggestion. Defaults to the default placeholder color. |
+| `suggestion` | `String` | The current suggestion shown. Read only. |
+| `suggestions` | `[String]` | Array of suggestions. |
+| `suggestionType` | `SuggestionType` | The type of suggestion that should be used. `.Word` will only hint the the next word in the suggestion and `.Sentence` will show the whole suggestion. Defaults to `.Sentence`. |
+| `pixelCorrections` | `CGPoint` | Move the suggestion label up/down left/right. Use this to correct any differences if the suggestion doesn't match the input value for some reason. |
+| `horizontalPadding` | `CGFloat` | Add padding to your textfield. Automatically set when using a `borderStyle` that has padding. |
+| `delimiter` | `String` | Add a delimiter to only show a suggestion if there's more than one occurance of the delimiter. Perfect for autocompleting email providers. |
-## Demo
+# Demo
-Check out the Example project.
+Check out the [example project](/Example).
-## Todo
-- [x] Smarter suggestions. If both John Doe and John Smith are in the suggestion array, only Joe should be suggested.
-- [ ] Tests
+# License
-## License
-AutocompleteField is provided under the [MIT License](http://http//opensource.org/licenses/mit-license.php). See LICENSE for details.
\ No newline at end of file
+`AutocompleteField` is provided under the [MIT License](http://http//opensource.org/licenses/mit-license.php). See LICENSE for details.
diff --git a/Sources/AutocompleteField.swift b/Sources/AutocompleteField.swift
new file mode 100644
index 0000000..3badbd0
--- /dev/null
+++ b/Sources/AutocompleteField.swift
@@ -0,0 +1,300 @@
+//
+// AutocompleteField.swift
+//
+// Created by Filip Stefansson on 2020-10-09.
+//
+
+import Foundation
+import UIKit
+
+public enum SuggestionType {
+ case Word
+ case Sentence
+}
+
+@IBDesignable public class AutocompleteField: UITextField
+{
+ // MARK: - public properties
+
+ /// The list of suggestions that the textfield should use when the user is typing.
+ public var suggestions: [String] = [] {
+ didSet {
+ self.suggestionList = self.allSuggestions()
+ }
+ }
+
+ /// The color of the autocompletion suggestion.
+ @IBInspectable public var suggestionColor: UIColor = UIColor(white: 0, alpha: 0.22)
+
+ /// The current suggestion shown. Read only.
+ public private(set) var suggestion: String?
+
+ /// Move the suggestion label in x or y. Sometimes there's a small difference between
+ /// the suggestion and the real text, and this can be used to fix it.
+ public var pixelCorrections: CGPoint = CGPoint(x: 0, y: 0);
+
+ /// The type of autocomplete that should be used
+ public var suggestionType: SuggestionType = .Sentence
+
+ /// Set a horizontal padding for the the textfield. Automatically set when using a `borderStyle` of `.roundedRect`, `.bezel` or `.line`, because those have added padding.
+ public var horizontalPadding: CGFloat = 0
+
+ /// Set a delimiter to only show suggestions after the first occurance of that character
+ public var delimiter: String?
+
+ // whenever the text value is set, we need to update the suggestion
+ // we do this by overriding the `text` property setter.
+ override public var text: String? {
+ didSet {
+ self.textFieldDidChange(self)
+ }
+ }
+
+ override public var borderStyle: UITextField.BorderStyle {
+ didSet {
+ setPadding()
+ }
+ }
+
+ // MARK: - private properties
+
+ private var suggestionList: [String] = []
+
+ /// The suggestion label
+ private var label = UILabel()
+
+ // MARK: - init functions
+
+ override public init(frame: CGRect)
+ {
+ super.init(frame: frame)
+ self.setup()
+ }
+
+ required public init?(coder aDecoder: NSCoder)
+ {
+ super.init(coder: aDecoder)
+ self.setup()
+ }
+
+ /// Create an instance of a AutocompleteField.
+ ///
+ /// - Parameters:
+ /// - frame: Frame of the textfield.
+ /// - suggestions: The list of suggestions that the textfield should use when the user is typing.
+ public init(frame: CGRect, suggestions: [String])
+ {
+ super.init(frame: frame)
+ self.suggestions = suggestions
+ self.setup()
+ }
+
+ // ovverride to set frame of the suggestion label whenever the textfield frame changes.
+ public override func layoutSubviews()
+ {
+ // use `horizontalPadding` and `pixelCorrections` to calculte new frame
+ self.label.frame = CGRect(x: self.horizontalPadding + self.pixelCorrections.x, y: self.pixelCorrections.y, width: self.frame.width - (self.horizontalPadding * 2), height: self.frame.height)
+ super.layoutSubviews()
+ }
+
+ // MARK: - private methods
+
+ private func setup() {
+ self.addTarget(self, action: #selector(AutocompleteField.textFieldDidChange(_:)), for: .editingChanged)
+
+ // create the label we use to
+ self.createAutocompleteLabel()
+ }
+
+ /// Sets up the suggestion label with the same font styling and alignment as the textfield.
+ private func createAutocompleteLabel()
+ {
+ self.label.lineBreakMode = .byClipping
+ self.setPadding()
+ self.addSubview(self.label)
+ }
+
+ private func setPadding() {
+ self.label.lineBreakMode = .byClipping
+
+ // if the textfield has one of the default styles,
+ // we need to add some padding, otherwise there will
+ // be a offset in x-led.
+ switch self.borderStyle
+ {
+ case .roundedRect, .bezel, .line:
+ self.horizontalPadding = 8
+ break
+ default:
+ break
+ }
+ }
+
+ /// Set content of the suggestion label.
+ ///
+ /// - parameters:
+ /// - text: Suggestion text
+ private func setLabelText(text: String?)
+ {
+ guard let labelText = text else {
+ label.attributedText = nil
+ return
+ }
+
+ // don't show the suggestion if
+ // 1. there's no text
+ // 2. the text is longer than the suggestion
+ if let inputText = self.text {
+ if (inputText.count < 1 || inputText.count >= labelText.count) {
+ label.attributedText = nil
+ return
+ }
+ }
+
+ let range = NSRange(location: 0, length: labelText.count);
+
+ // create an attributed string instead of the regular one
+ // in this way we can hide the letters in the suggestion
+ // that the user has already written
+ let attributedString = NSMutableAttributedString(
+ string: labelText
+ )
+ attributedString.addAttributes(self.defaultTextAttributes, range: range)
+ attributedString.addAttribute(.foregroundColor, value: self.suggestionColor, range: range);
+
+ // hide the letters that are under the fields text
+ // if the suggestion is abcdefgh and the user has written abcd
+ // we want to hide those letters from the suggestion
+ if let inputText = self.text
+ {
+ attributedString.addAttribute(NSAttributedString.Key.foregroundColor,
+ value: UIColor.clear,
+ range: NSRange(location: 0, length: inputText.count)
+ )
+ }
+
+ label.attributedText = attributedString
+ label.textAlignment = self.textAlignment
+ }
+
+ /// This method converts the suggestions list if suggestionType is sentence
+ /// and an array with step of an suggestion if suggestionType is word
+ private func allSuggestions() -> [String] {
+ if (self.suggestionType == .Sentence) {
+ return self.suggestions
+ }
+
+ var wordSuggestions: [String] = [];
+
+ for suggestion in suggestions {
+ let suggestions = suggestion.components(separatedBy: " ")
+ for count in 0...suggestions.count - 1 {
+ // the string where we will append all items
+ var name = ""
+ for i in 0...count {
+ if (i > 0) {
+ name = name.appending(" ")
+ }
+ name = name.appending(suggestions[i])
+ }
+ wordSuggestions.append(contentsOf: [name])
+ }
+ }
+
+ wordSuggestions = wordSuggestions.sorted { $0.count < $1.count }
+
+ return wordSuggestions
+ }
+
+ /// Splits text by a delimiter and returns an array with a max of two items containing everything
+ /// before and after the first occurance of the delimiter
+ /// test@em@il.com becomes ["test", "em@ail.com"]
+ private func splitTextByDelimiter(text: String, delimiter: String) -> [String] {
+ var parts = text.components(separatedBy: delimiter)
+ let firstPart = parts[0]
+ if (parts.count > 1) {
+ parts.removeFirst()
+ return [firstPart, parts.joined(separator: delimiter)]
+ }
+ return [firstPart]
+ }
+
+ /// Scans through the suggestions array and finds a suggestion that matches the searchTerm.
+ ///
+ /// - parameters:
+ /// - searchTerm: what to search for
+ /// - returns A string or nil
+ private func getSuggestion(text: String?) -> String?
+ {
+ guard var inputText = text else {
+ return nil;
+ }
+
+ // if delimiter is set
+ if let delimiterText = self.delimiter {
+ // check if delimiter has been used
+ let parts = self.splitTextByDelimiter(text: inputText, delimiter: delimiterText)
+ if (parts.count > 1) {
+ inputText = parts[1]
+ } else {
+ return nil
+ }
+ }
+
+ if (inputText == "") {
+ return nil;
+ }
+
+ if let suggestion = self.suggestionList.first(where: { $0.hasPrefix(inputText) }) {
+ return suggestion
+ }
+
+ return nil
+ }
+
+ // MARK: - Events
+
+ /// Triggered whenever the field text changes.
+ ///
+ /// - parameters:
+ /// - notification: The NSNotifcation attached to the event
+ @objc func textFieldDidChange(_ textField: UITextField) {
+ guard let text = self.text else {
+ return
+ }
+
+ if var suggestion = getSuggestion(text: text) {
+ // extra logic if we have a delimiter
+ if let delimter = self.delimiter {
+ // grab first part of text
+ let prefix = self.splitTextByDelimiter(text: text, delimiter: delimter)[0]
+ // add everything before the delimiter to the suggestion so it
+ // works with our setLabelText method
+ suggestion = prefix.appending(delimter).appending(suggestion)
+ }
+
+ self.suggestion = suggestion
+ self.setLabelText(text: suggestion)
+ } else {
+ self.suggestion = nil
+ self.setLabelText(text: nil)
+ }
+ }
+
+ public override func textRect(forBounds bounds: CGRect) -> CGRect {
+ return CGRect(x: bounds.origin.x + self.horizontalPadding, y: bounds.origin.y, width: bounds.size.width - (self.horizontalPadding * 2), height: bounds.size.height);
+ }
+
+ public override func editingRect(forBounds bounds: CGRect) -> CGRect {
+ return self.textRect(forBounds: bounds)
+ }
+
+ public override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
+ return self.textRect(forBounds: bounds)
+ }
+
+ // remove target on deinit
+ deinit {
+ self.removeTarget(self, action: #selector(AutocompleteField.textFieldDidChange(_:)), for: .editingChanged)
+ }
+}
diff --git a/Tests/Info.plist b/Tests/Info.plist
new file mode 100644
index 0000000..64d65ca
--- /dev/null
+++ b/Tests/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+
diff --git a/Tests/Tests.swift b/Tests/Tests.swift
new file mode 100644
index 0000000..fe126ce
--- /dev/null
+++ b/Tests/Tests.swift
@@ -0,0 +1,75 @@
+import XCTest
+
+final class AutocompleteFieldTests: XCTestCase {
+ func testWordSuggestions() {
+ let textfield = AutocompleteField()
+ textfield.suggestionType = .Word
+ textfield.suggestions = [
+ "George Washington",
+ "Thomas Jefferson",
+ "John Adams",
+ "Theodore Roosevelt",
+ "John F. Kennedy",
+ "George W. Bush",
+ ]
+
+ textfield.text = "Geo"
+ XCTAssertEqual(textfield.suggestion, "George")
+
+ textfield.text = "Ada"
+ XCTAssertEqual(textfield.suggestion, nil)
+
+ textfield.text = "George W"
+ XCTAssertEqual(textfield.suggestion, "George W.")
+
+ textfield.text = "George W. "
+ XCTAssertEqual(textfield.suggestion, "George W. Bush")
+
+ textfield.text = "John Adams"
+ XCTAssertEqual(textfield.suggestion, "John Adams")
+
+ textfield.text = "John Adams A"
+ XCTAssertEqual(textfield.suggestion, nil)
+
+ textfield.text = ""
+ XCTAssertEqual(textfield.suggestion, nil)
+ }
+
+ func testSetenceSuggestions() {
+ let textfield = AutocompleteField()
+ textfield.suggestions = [
+ "George Washington",
+ "Thomas Jefferson",
+ "John Adams",
+ "Theodore Roosevelt",
+ "John F. Kennedy",
+ "George W. Bush",
+ ]
+
+ textfield.text = "Geo"
+ XCTAssertEqual(textfield.suggestion, "George Washington")
+
+ textfield.text = "Ada"
+ XCTAssertEqual(textfield.suggestion, nil)
+
+ textfield.text = "George W"
+ XCTAssertEqual(textfield.suggestion, "George Washington")
+
+ textfield.text = "George W. "
+ XCTAssertEqual(textfield.suggestion, "George W. Bush")
+
+ textfield.text = "John Adams"
+ XCTAssertEqual(textfield.suggestion, "John Adams")
+
+ textfield.text = "John Adams A"
+ XCTAssertEqual(textfield.suggestion, nil)
+
+ textfield.text = ""
+ XCTAssertEqual(textfield.suggestion, nil)
+ }
+
+ static var allTests = [
+ ("testWordSuggestions", testWordSuggestions),
+ ("testSetenceSuggestions", testSetenceSuggestions),
+ ]
+}