87 lines
2.5 KiB
Swift
87 lines
2.5 KiB
Swift
import SwiftUI
|
|||
|
|
|
||
|
|
/// A view modifier that intercepts newlines in a text field and transfers focus to another field.
|
||
|
|
///
|
||
|
|
/// This modifier monitors text changes in a `TextField` component and prevents newlines
|
||
|
|
/// from being added. Instead, when a newline is detected, it updates the focus state to move to
|
||
|
|
/// a specified field.
|
||
|
|
struct OnNewlineFocusModifier<Field: Hashable>: ViewModifier {
|
||
|
|
|
||
|
|
// MARK: Properties
|
||
|
|
|
||
|
|
/// Binding to the text being edited.
|
||
|
|
@Binding
|
||
|
|
private var text: String
|
||
|
|
|
||
|
|
/// Binding to the focus state.
|
||
|
|
private let focusState: FocusState<Field?>.Binding
|
||
|
|
|
||
|
|
/// The field to focus when a newline is detected.
|
||
|
|
private let targetField: Field
|
||
|
|
|
||
|
|
// MARK: Initializers
|
||
|
|
|
||
|
|
/// Creates a modifier that transfers focus on newline.
|
||
|
|
///
|
||
|
|
/// - Parameters:
|
||
|
|
/// - text: A binding to the text being edited.
|
||
|
|
/// - focusState: A binding to the focus state.
|
||
|
|
/// - targetField: The field to focus when a newline is detected.
|
||
|
|
init(
|
||
|
|
text: Binding<String>,
|
||
|
|
focusState: FocusState<Field?>.Binding,
|
||
|
|
targetField: Field
|
||
|
|
) {
|
||
|
|
self._text = text
|
||
|
|
|
||
|
|
self.focusState = focusState
|
||
|
|
self.targetField = targetField
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: Body
|
||
|
|
|
||
|
|
/// The content and behavior of the view modifier.
|
||
|
|
func body(content: Content) -> some View {
|
||
|
|
content
|
||
|
|
.onChange(of: text) { oldValue, newValue in
|
||
|
|
guard newValue.contains(.newline) else {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
text = oldValue
|
||
|
|
focusState.wrappedValue = targetField
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - View Extension
|
||
|
|
|
||
|
|
extension View {
|
||
|
|
|
||
|
|
/// Transfers focus to another field when the user presses return in a text field.
|
||
|
|
///
|
||
|
|
/// This modifier prevents newlines from being added and instead moves keyboard focus
|
||
|
|
/// to the specified field. Useful for creating a form-like flow between text fields.
|
||
|
|
///
|
||
|
|
/// - Parameters:
|
||
|
|
/// - text: A binding to the text being edited.
|
||
|
|
/// - field: The field to focus when return is pressed.
|
||
|
|
/// - focusState: A binding to the focus state.
|
||
|
|
/// - Returns: A modified view with focus transfer behavior.
|
||
|
|
func onNewlineFocus<Field: Hashable>(
|
||
|
|
_ text: Binding<String>,
|
||
|
|
to field: Field,
|
||
|
|
on focusState: FocusState<Field?>.Binding
|
||
|
|
) -> some View {
|
||
|
|
modifier(
|
||
|
|
OnNewlineFocusModifier(
|
||
|
|
text: text,
|
||
|
|
focusState: focusState,
|
||
|
|
targetField: field
|
||
|
|
)
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|