Skip to content

Commit

Permalink
Generic Placeholders
Browse files Browse the repository at this point in the history
  • Loading branch information
dreymonde committed Apr 17, 2017
1 parent 28d03f7 commit 72aea1a
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 33 deletions.
40 changes: 31 additions & 9 deletions Placeholders/Placeholders+UIKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@

import UIKit

public protocol TextFieldPlaceholder {

func set(on textField: UITextField)

}

extension String : TextFieldPlaceholder {

public func set(on textField: UITextField) {
textField.placeholder = self
}

}

extension NSAttributedString : TextFieldPlaceholder {

public func set(on textField: UITextField) {
textField.attributedPlaceholder = self
}

}

extension UITextField {

var isTextEmpty: Bool {
Expand All @@ -17,16 +39,16 @@ extension UITextField {
return true
}

public struct PlaceholderChange {
public struct PlaceholderChange<Placeholder : TextFieldPlaceholder> {

private let _setNewPlaceholder: (String, UITextField) -> ()
private let _setNewPlaceholder: (Placeholder, UITextField) -> ()

public func setNewPlaceholder(_ placeholder: String, on textField: UITextField) {
public func setNewPlaceholder(_ placeholder: Placeholder, on textField: UITextField) {
guard textField.isTextEmpty else { return }
_setNewPlaceholder(placeholder, textField)
}

public init(setNewPlaceholder: @escaping (String, UITextField) -> ()) {
public init(setNewPlaceholder: @escaping (Placeholder, UITextField) -> ()) {
self._setNewPlaceholder = setNewPlaceholder
}

Expand All @@ -36,11 +58,11 @@ extension UITextField {

extension UITextField.PlaceholderChange {

public static func caTransition(_ transition: @escaping () -> CATransition) -> UITextField.PlaceholderChange {
public static func caTransition(_ transition: @escaping () -> CATransition) -> UITextField.PlaceholderChange<Placeholder> {
return UITextField.PlaceholderChange { (placeholder, textField) in
let transition = transition()
textField.subviews.first(where: { NSStringFromClass(type(of: $0)) == "UITextFieldLabel" })?.layer.add(transition, forKey: nil)
textField.placeholder = placeholder
placeholder.set(on: textField)
}
}

Expand All @@ -66,7 +88,7 @@ extension UITextField.PlaceholderChange {

public static func pushTransition(_ direction: TransitionPushDirection,
duration: TimeInterval = 0.35,
timingFunction: CAMediaTimingFunction = .init(name: kCAMediaTimingFunctionEaseInEaseOut)) -> UITextField.PlaceholderChange {
timingFunction: CAMediaTimingFunction = .init(name: kCAMediaTimingFunctionEaseInEaseOut)) -> UITextField.PlaceholderChange<Placeholder> {
return .caTransition {
let transition = CATransition()
transition.duration = duration
Expand All @@ -79,12 +101,12 @@ extension UITextField.PlaceholderChange {

}

extension Placeholders {
extension Placeholders where Element : TextFieldPlaceholder {

public func start(interval: TimeInterval,
fireInitial: Bool = true,
textField: UITextField,
animation change: UITextField.PlaceholderChange) {
animation change: UITextField.PlaceholderChange<Element>) {
self.start(interval: interval, fireInitial: fireInitial) { [weak textField, weak self] (placeholder) in
if let textField = textField {
change.setNewPlaceholder(placeholder, on: textField)
Expand Down
38 changes: 19 additions & 19 deletions Placeholders/Placeholders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,30 @@

import Foundation

final public class Placeholders {
public struct PlaceholdersOptions : OptionSet {

public var iterator: AnyIterator<String>
public var rawValue: Int

public init(rawValue: Int) {
self.rawValue = rawValue
}

public static var infinite = PlaceholdersOptions(rawValue: 1 << 0)
public static var shuffle = PlaceholdersOptions(rawValue: 1 << 1)

}

final public class Placeholders<Element> {

public var iterator: AnyIterator<Element>
var timer: Timer?
var action: (String) -> () = { _ in }
var action: (Element) -> () = { _ in }

public init<StringIterator : IteratorProtocol>(iterator: StringIterator) where StringIterator.Element == String {
public init<Iterator : IteratorProtocol>(iterator: Iterator) where Iterator.Element == Element {
self.iterator = AnyIterator(iterator)
}

public convenience init(placeholders: [String], options: Options = []) {
public convenience init(placeholders: [Element], options: PlaceholdersOptions = []) {
var finalPlaceholders = placeholders
if options.contains(.shuffle) { finalPlaceholders.shuffle() }
if options.contains(.infinite) {
Expand All @@ -28,24 +41,11 @@ final public class Placeholders {
}
}

public struct Options : OptionSet {

public var rawValue: Int

public init(rawValue: Int) {
self.rawValue = rawValue
}

public static var infinite = Options(rawValue: 1 << 0)
public static var shuffle = Options(rawValue: 1 << 1)

}

deinit {
timer?.invalidate()
}

public func start(interval: TimeInterval, fireInitial: Bool, action: @escaping (String) -> ()) {
public func start(interval: TimeInterval, fireInitial: Bool, action: @escaping (Element) -> ()) {
self.action = action
let timer = Timer(timeInterval: interval,
target: self,
Expand Down
46 changes: 46 additions & 0 deletions PlaceholdersTests/PlaceholdersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,35 @@
import XCTest
@testable import Placeholders

extension UITextField.PlaceholderChange {

static var fade_a: UITextField.PlaceholderChange<Placeholder> {
return UITextField.PlaceholderChange { (placeholder, textField) in
let transition = CATransition()
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
textField.subviews.first(where: { NSStringFromClass(type(of: $0)) == "UITextFieldLabel" })?.layer.add(transition, forKey: nil)
placeholder.set(on: textField)
}
}

static var fade: UITextField.PlaceholderChange<Placeholder> {
return .caTransition {
let transition = CATransition()
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
return transition
}
}

}

class PlaceholdersTests: XCTestCase {

let placeholders = Placeholders(placeholders: ["First", "Second", "Third"], options: [.infinite, .shuffle])

override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
Expand All @@ -26,6 +53,25 @@ class PlaceholdersTests: XCTestCase {
// Use XCTAssert and related functions to verify your tests produce the correct results.
}

func testREADME2() {
let textField = UITextField()
placeholders.start(interval: 3.0,
fireInitial: true,
textField: textField,
animation: .pushTransition(.fromBottom))
placeholders.start(interval: 3.0,
fireInitial: true,
textField: textField,
animation: .fade)
}

func testREADME1() {
let placeholders = Placeholders(placeholders: ["A", "B", "C"], options: .infinite)
placeholders.start(interval: 2.0, fireInitial: true, action: { next in
print(next)
})
}

func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
Expand Down
31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Placeholders

[![Swift][swift-badge]][swift-url]
[![Platform][platform-badge]][platform-url]

**Placeholders** gives you the ability to define multiple placeholders for `UITextField`, and also animate their change in the way you like. The result looks like that:

![Demo](Resources/Demo.gif)
Expand Down Expand Up @@ -50,26 +54,28 @@ To define your custom `animation` as, for example, `.pushTransition`, you can ex
```swift
extension UITextField.PlaceholderChange {

static var fade: UITextField.PlaceholderChange {
static var fade: UITextField.PlaceholderChange<Placeholder> {
return UITextField.PlaceholderChange { (placeholder, textField) in
let transition = CATransition()
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
textField.subviews.first(where: { NSStringFromClass(type(of: $0)) == "UITextFieldLabel" })?.layer.add(transition, forKey: nil)
textField.placeholder = placeholder
placeholder.set(on: textField)
}
}

}
```

Or you can use convenience `.caTransition` static function to make your life a bit easier:
This generic `Placeholder` type and `placeholder.set(on: textField)` syntax exists in order to support `NSAttributedString` as a placeholder.

You can also use convenience `.caTransition` static function to make your life a bit easier:

```swift
extension UITextField.PlaceholderChange {

static var fade: UITextField.PlaceholderChange {
static var fade: UITextField.PlaceholderChange<Placeholder> {
return .caTransition {
let transition = CATransition()
transition.duration = 0.35
Expand All @@ -91,4 +97,19 @@ placeholders.start(interval: 3.0,
animation: .fade)
```

Neat!
Neat!

## Installation
**Placeholders** is available through [Carthage][carthage-url]. To install, just write into your Cartfile:

```ruby
github "dreymonde/Placeholders" ~> 0.1.0
```

We also encourage you to write your very own implementation that fits your needs best. Our source code is there to help.

[carthage-url]: https://github.com/Carthage/Carthage
[swift-badge]: https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat
[swift-url]: https://swift.org
[platform-badge]: https://img.shields.io/badge/Platform-iOS-lightgray.svg?style=flat
[platform-url]: https://developer.apple.com/swift/

0 comments on commit 72aea1a

Please sign in to comment.