适用于 SwiftUI 的强大、流畅且紧凑的自定义文本字段
Sheathed-TextField-SwiftUI
笔比剑更强大!
此自定义文本字段是一个非常独特且流畅的核心,适用于使用 SwiftUI 构建的任何表单或搜索菜单。它被设计成尽可能引人注目和实用,这个文本字段的每个元素不仅增加了风格价值,还增加了目的。从名字中你可以猜到命名约定是从剑派生出来的,因为文本字段的过渡类似于剑的出鞘;高精度、张扬、目的性强;您正在发现一个非常重要的工具,即应用程序的个性化网关。
Supported Platforms:
- iOS
Minimum Required iOS Version:
- iOS 16
Minimum Required Xcode / Swift Version to build this package:
- Xcode 14 / Swift: 5.7
Installation:
- Launch your target Xcode project
- Go to the menu bar
- Click
File -> Add Packages Packages -> Search bar
- Enter the URL of this repository
https://github.com/jcook03266/Sheathed-TextField-SwiftUI/
- Set the dependency rule to
Up to Next Major Version
- Press the button at the bottom of the window.
Add Package
- Done!
How to use this package in your code:
- At the top of the swift files in which this textfield will be referenced, add:
import Sheathed_TextField_SwiftUI
- Define a
SheathedTextFieldModel
- Configure the model using the built in function
configurator
- Define a view and insert the configured model into that view
SheathedTextField
- Install the inside of a parent view, Note: The textfield is already centered, and it has a device appropriate width you can also specify in the view’s constructor
SheathedTextField
init
Example:
View Model:
import SwiftUI
import Sheathed_TextField_SwiftUI
class LoginScreenViewModel: ObservableObject {
// MARK: - Published / TextField Models
@Published var username_emailTextFieldModel: SheathedTextFieldModel!
@Published var passwordTextFieldModel: SheathedTextFieldModel!
...
init(...) {
setModels()
}
func setModels() {
username_emailTextFieldModel = .init()
username_emailTextFieldModel.configurator { [weak self] model in
guard let self = self else { return }
model.title = self.username_emailTextFieldTitle
model.placeholderText = "user123 / sampleEmail@Test.com"
model.icon = Icons.getIconImage(named: .person_fill)
model.keyboardType = .emailAddress
model.textContentType = .emailAddress
model.submitLabel = .next
model.onSubmitAction = { [weak self] in
guard let self = self else { return }
self.passwordTextFieldModel.focus()
}
}
passwordTextFieldModel = .init()
passwordTextFieldModel.configurator { [weak self] model in
guard let self = self else { return }
model.title = self.passwordTextFieldTitle
model.placeholderText = "Cr3&TiV3Password!23"
model.icon = Icons.getIconImage(named: .lock_fill)
model.keyboardType = .asciiCapable
model.textContentType = .password
model.submitLabel = .done
// Validation
model.entryValidationEnabled = true
model.validationCondition = { text in
!text.contains {
$0 == "."
}
}
model.inFieldButtonIcon = Icons.getIconImage(named: .eye_slash)
model.protected = true
model.inFieldButtonAction = {
model.protected.toggle()
model.inFieldButtonIcon = model.protected ? Icons.getIconImage(named: .eye_slash) : Icons.getIconImage(named: .eye)
}
}
}
View:
import SwiftUI
import Sheathed_TextField_SwiftUI
// MARK: - Observed
@StateObject var model: LoginScreenViewModel
...
struct LoginScreen: View {
// TextFields
var username_EmailTextField: some View {
let textField = SheathedTextField(model: model.username_emailTextFieldModel)
return textField
}
var password_TextField: some View {
let textField = SheathedTextField(model: model.passwordTextFieldModel)
return textField
}
var textFields: some View {
VStack(spacing: textFieldSpacing) {
username_EmailTextField
password_TextField
}
}
var body: some View {
GeometryReader { geom in
ScrollView {
VStack {
...
textFields
}
.frame(width: geom.size.width)
...
}
.submitScope(true) // Note: Prevents submissions from this page from backtracking and triggering other submissions from separate views in the hierarchy
...
}
}
...
}
Brief Overview of necessary property wrappers:
Sheathed-TextField
models must be marked as when embedded in a view model, or when they’re embedded in aPublished
StateObject
View
- In order for the parent view to respond to changes in this textfield, new events have to be emitted by a and broadcasted through an , or listened to as an observable object by a view, namely as a state object to prevent a loss of state when the view reloads given a new value emitted by the model
Publisher
ObservableObject
- It’s not recommended to embed the textfield model directly into a view, for lack of simplicity. The model should be housed in a view model in order to centralize all emissions from important objects currently present in the scene.
- Adhere to the functionality of SwiftUI property wrappers and you’re good to go!
Extended Information / Q&A:
How do I access the text I enter?
[textFieldModel].textEntry
How can I focus the text field from an external source?
[textFieldModel].focus()
Why does my text clear upon editing past content when the textfield is marked as protected?
This is expected behavior because a secure text field clears past text when you go back to edit it.
How do I validate a text entry?
Specify a validation condition to be executed every time the text entry changes, and set entryValidationEnabled to to enable validationTrue
[textFieldModel].entryValidationEnabled = true
// This is just an example, use REGEX to properly validate the text input in this closure
[textFieldModel].validationCondition = { text in
!text.contains {
$0 == "." || $0 == "/"
}
}
How do I trigger an ‘unsheathe’ when an icon view isn’t present / when I don’t define a side icon?
You can trigger an unsheathe action at any time by long pressing the textfield, this is also possible when an icon is present. When the icon view is rendered you can simply tap on it to trigger this event, but, this same functionality isn’t available for the textfield component of the view because all touches will be eaten up by the listener.onTap
Demonstration
Here’s a demo of a login form built using two sheathed textfields in a VStack. Auto-fill functionality works as expected with this custom textfield.
Demo.mp4
Support
If you really enjoyed this simple library, then consider checking out my other work, and or supporting me through my sponsor links on the side menu. Thanks for reading; keep calm and code on! <3