Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
filipstefansson committed Nov 8, 2015
0 parents commit 788d8d5
Show file tree
Hide file tree
Showing 13 changed files with 1,140 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
246 changes: 246 additions & 0 deletions AutocompleteField/AutocompleteField.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
//
// AutocompleteField.swift
// Example
//
// Created by Filip Stefansson on 05/11/15.
// Copyright © 2015 Filip Stefansson. All rights reserved.
//

import Foundation
import UIKit


@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)
}
}
}


// 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(text : String = "")
{
// label string
if(text.characters.count < 1) {
label.attributedText = nil
return
}

// 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<String.Index>(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)
}
}
31 changes: 31 additions & 0 deletions Example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 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
Loading

0 comments on commit 788d8d5

Please sign in to comment.