Install via SPM per usual. Supports iOS 13+. MIT License.
Last active
February 19, 2022 04:31
-
-
Save ericlewis/5362ab3634aa523214e25a3ece99dd4d to your computer and use it in GitHub Desktop.
Experimental library for creating useful TextFieldStyles in SwiftUI.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Introspect | |
public protocol BetterTextFieldStyle: TextFieldStyle {} | |
extension View { | |
public func textFieldStyle<S: BetterTextFieldStyle>(_ style: S) -> some View { | |
textFieldStyle(CombinedStyle(style)) | |
} | |
} | |
@propertyWrapper | |
public struct TextFieldState<Value>: DynamicProperty { | |
@Environment(\.textFieldState) | |
private var state | |
private let keyPath: KeyPath<FieldState, Value> | |
public init() where Value == FieldState { | |
self.keyPath = \.self | |
} | |
public init(_ keyPath: KeyPath<FieldState, Value>) { | |
self.keyPath = keyPath | |
} | |
public var wrappedValue: Value { | |
get { state[keyPath: keyPath] } | |
} | |
} | |
public struct FieldState { | |
public var isEditing: Bool = false | |
public var text: Binding<String> | |
struct Key: EnvironmentKey { | |
static var defaultValue: FieldState = .init(text: .constant("")) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
extension EnvironmentValues { | |
var textFieldState: FieldState { | |
get { self[FieldState.Key.self] } | |
set { self[FieldState.Key.self] = newValue } | |
} | |
} | |
struct CombinedStyle<S: TextFieldStyle>: TextFieldStyle { | |
let style: S | |
init(_ style: S) { | |
self.style = style | |
} | |
func _body(configuration: TextField<_Label>) -> some View { | |
configuration | |
.textFieldStyle(CoordinatorStyle()) | |
.textFieldStyle(style) | |
} | |
} | |
struct CoordinatorStyle: TextFieldStyle { | |
@StateObject | |
private var coordinator = Coordinator() | |
func _body(configuration: TextField<_Label>) -> some View { | |
configuration | |
.environment(\.textFieldState, .init( | |
isEditing: coordinator.isEditing, | |
text: Mirror(reflecting: configuration).descendant("_text") as! Binding<String>) | |
) | |
.introspectTextField { textField in | |
coordinator.textField = textField | |
} | |
} | |
class Coordinator: NSObject, ObservableObject, UITextFieldDelegate { | |
@Published | |
private(set) var isEditing: Bool = false | |
weak var textField: UITextField? { | |
didSet { | |
if delegate == nil, let delegate = textField?.delegate { | |
self.delegate = delegate | |
textField?.delegate = self | |
} | |
} | |
} | |
weak var delegate: UITextFieldDelegate? | |
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { | |
delegate?.textFieldShouldBeginEditing?(textField) ?? true | |
} | |
func textFieldDidBeginEditing(_ textField: UITextField) { | |
isEditing = true | |
delegate?.textFieldDidBeginEditing?(textField) | |
} | |
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { | |
delegate?.textFieldShouldEndEditing?(textField) ?? true | |
} | |
func textFieldDidEndEditing(_ textField: UITextField) { | |
isEditing = false | |
delegate?.textFieldDidEndEditing?(textField) | |
} | |
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) { | |
isEditing = false | |
delegate?.textFieldDidEndEditing?(textField, reason: reason) | |
} | |
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { | |
delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) ?? true | |
} | |
func textFieldDidChangeSelection(_ textField: UITextField) { | |
delegate?.textFieldDidChangeSelection?(textField) | |
} | |
func textFieldShouldClear(_ textField: UITextField) -> Bool { | |
delegate?.textFieldShouldClear?(textField) ?? false | |
} | |
func textFieldShouldReturn(_ textField: UITextField) -> Bool { | |
delegate?.textFieldShouldReturn?(textField) ?? true | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// swift-tools-version: 5.6 | |
import PackageDescription | |
let package = Package( | |
name: "swiftui-better-textfieldstyle", | |
platforms: [.iOS(.v13)], | |
products: [ | |
.library( | |
name: "BetterTextFieldStyle", | |
targets: ["BetterTextFieldStyle"]), | |
], | |
dependencies: [ | |
.package(url: "https://github.com/siteline/SwiftUI-Introspect", from: "0.1.4") | |
], | |
targets: [ | |
.target( | |
name: "BetterTextFieldStyle", | |
dependencies: [ | |
.product(name: "Introspect", package: "swiftui-introspect") | |
], | |
path: ".", | |
exclude: ["Package.swift"] | |
) | |
] | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment