82 lines
2.4 KiB
Swift
82 lines
2.4 KiB
Swift
import SwiftUI
|
|||
|
|
|
||
|
|
/// A view modifier that clears text and removes focus when return is pressed on empty content.
|
||
|
|
///
|
||
|
|
/// This modifier monitors text changes and provides smart handling of newlines:
|
||
|
|
/// - Empty text is allowed normally
|
||
|
|
/// - Newlines at the end of whitespace-only text clear the field and remove focus
|
||
|
|
/// - Multi-line content with actual text is preserved normally
|
||
|
|
struct OnEmptyNewlineResignFocusModifier<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
|
||
|
|
|
||
|
|
// MARK: Initializers
|
||
|
|
|
||
|
|
/// Creates a modifier that clears text on empty newline.
|
||
|
|
///
|
||
|
|
/// - Parameters:
|
||
|
|
/// - text: A binding to the text being edited.
|
||
|
|
/// - focusState: A binding to the focus state.
|
||
|
|
init(
|
||
|
|
text: Binding<String>,
|
||
|
|
focusState: FocusState<Field?>.Binding
|
||
|
|
) {
|
||
|
|
self._text = text
|
||
|
|
self.focusState = focusState
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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.isEmpty == false else {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if newValue.last == .newline {
|
||
|
|
if newValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||
|
|
text = ""
|
||
|
|
focusState.wrappedValue = nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - View Extension
|
||
|
|
|
||
|
|
extension View {
|
||
|
|
|
||
|
|
/// Clears the text and removes focus when return is pressed on empty content.
|
||
|
|
///
|
||
|
|
/// This modifier allows users to dismiss an optional text field by pressing return
|
||
|
|
/// when the field is empty or contains only whitespace. Multi-line content is preserved normally.
|
||
|
|
///
|
||
|
|
/// - Parameters:
|
||
|
|
/// - focusState: A binding to the focus state.
|
||
|
|
/// - text: A binding to the text being edited.
|
||
|
|
/// - Returns: A modified view with clear-on-empty behavior.
|
||
|
|
func onEmptyNewlineResignFocus<Field: Hashable>(
|
||
|
|
text: Binding<String>,
|
||
|
|
from focusState: FocusState<Field?>.Binding
|
||
|
|
) -> some View {
|
||
|
|
modifier(
|
||
|
|
OnEmptyNewlineResignFocusModifier(
|
||
|
|
text: text,
|
||
|
|
focusState: focusState
|
||
|
|
)
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|