Files
todos/App/Sources/View Modifiers/OnEmptyNewlineResignFocusModifier.swift
T

82 lines
2.4 KiB
Swift
Raw Normal View History

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
)
)
}
}