Files

87 lines
2.5 KiB
Swift
Raw Permalink Normal View History

2026-02-12 01:21:19 +00:00
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
)
)
}
}