diff --git a/Placeholders/Placeholders+UIKit.swift b/Placeholders/Placeholders+UIKit.swift index 36f327a..2a0f2af 100644 --- a/Placeholders/Placeholders+UIKit.swift +++ b/Placeholders/Placeholders+UIKit.swift @@ -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 { @@ -17,16 +39,16 @@ extension UITextField { return true } - public struct PlaceholderChange { + public struct PlaceholderChange { - 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 } @@ -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 { 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) } } @@ -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 { return .caTransition { let transition = CATransition() transition.duration = duration @@ -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) { self.start(interval: interval, fireInitial: fireInitial) { [weak textField, weak self] (placeholder) in if let textField = textField { change.setNewPlaceholder(placeholder, on: textField) diff --git a/Placeholders/Placeholders.swift b/Placeholders/Placeholders.swift index 50557f3..20794cc 100644 --- a/Placeholders/Placeholders.swift +++ b/Placeholders/Placeholders.swift @@ -8,17 +8,30 @@ import Foundation -final public class Placeholders { +public struct PlaceholdersOptions : OptionSet { - public var iterator: AnyIterator + 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 { + + public var iterator: AnyIterator var timer: Timer? - var action: (String) -> () = { _ in } + var action: (Element) -> () = { _ in } - public init(iterator: StringIterator) where StringIterator.Element == String { + public init(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) { @@ -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, diff --git a/PlaceholdersTests/PlaceholdersTests.swift b/PlaceholdersTests/PlaceholdersTests.swift index 536cbed..3af02ed 100644 --- a/PlaceholdersTests/PlaceholdersTests.swift +++ b/PlaceholdersTests/PlaceholdersTests.swift @@ -9,8 +9,35 @@ import XCTest @testable import Placeholders +extension UITextField.PlaceholderChange { + + static var fade_a: UITextField.PlaceholderChange { + 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 { + 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. @@ -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 { diff --git a/README.md b/README.md index f66f7bf..1646393 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 { 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 { return .caTransition { let transition = CATransition() transition.duration = 0.35 @@ -91,4 +97,19 @@ placeholders.start(interval: 3.0, animation: .fade) ``` -Neat! \ No newline at end of file +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/