From 5a294a710f64c2406f86ed6f5357edf97c097cbb Mon Sep 17 00:00:00 2001 From: Shaps Benkau Date: Mon, 8 May 2023 13:23:35 +0100 Subject: [PATCH] Introduces a backport for TextInputCapitalization --- .../AutoCapitalization.swift | 82 ++++++++++++++++--- .../SwiftUIBackports/iOS/Submit/Submit.swift | 4 +- .../iOS/TextEditor/TextEditor.swift | 5 +- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/Sources/SwiftUIBackports/iOS/AutoCapitalization/AutoCapitalization.swift b/Sources/SwiftUIBackports/iOS/AutoCapitalization/AutoCapitalization.swift index e1023392..2a10e74c 100644 --- a/Sources/SwiftUIBackports/iOS/AutoCapitalization/AutoCapitalization.swift +++ b/Sources/SwiftUIBackports/iOS/AutoCapitalization/AutoCapitalization.swift @@ -2,22 +2,64 @@ import SwiftUI import SwiftBackports #if os(iOS) -extension Backport where Wrapped: View { +@available(iOS, deprecated: 15) +public extension Backport where Wrapped: View { + /// Sets how often the shift key in the keyboard is automatically enabled. + /// + /// Use `backport.textInputAutocapitalization(_:)` when you need to automatically + /// capitalize words, sentences, or other text like proper nouns. + /// + /// In example below, as the user enters text the shift key is + /// automatically enabled before every word: + /// + /// TextField("Last, First", text: $fullName) + /// .backport.textInputAutocapitalization(.words) + /// + /// The ``TextInputAutocapitalization`` struct defines the available + /// autocapitalizing behavior. Providing `nil` to this view modifier does + /// not change the autocapitalization behavior. The default is + /// `Backport.TextInputAutocapitalization.sentences`. + /// + /// - Parameter autocapitalization: One of the capitalizing behaviors + /// defined in the `Backport.TextInputAutocapitalization` struct or nil. + @ViewBuilder func textInputAutocapitalization(_ autocapitalization: Backport.TextInputAutocapitalization?) -> some View { - wrapped.modifier( - AutoCapitalizationModifier( - capitalization: autocapitalization?.capitalization ?? .none - ) - ) + Group { + if #available(iOS 16, *) { + var type: SwiftUI.TextInputAutocapitalization { + switch autocapitalization { + case .none: + return .sentences + case .some(let wrapped): + switch wrapped { + case .never: return .never + case .words: return .words + case .sentences: return .sentences + case .characters: return .characters + default: return .sentences + } + } + } + wrapped.textInputAutocapitalization(type) + } else { + wrapped.modifier( + AutoCapitalizationModifier( + capitalization: autocapitalization?.capitalization ?? .none + ) + ) + } + } + .environment(\.textInputAutocapitalization, autocapitalization) } } -@available(iOS, introduced: 13, deprecated: 15) -@available(macOS, unavailable) -@available(tvOS, unavailable) -@available(watchOS, unavailable) +@available(iOS, deprecated: 15) extension Backport { - public struct TextInputAutocapitalization { + /// The kind of autocapitalization behavior applied during text input. + /// + /// Pass an instance of `Backport.TextInputAutocapitalization` to the + /// ``View/backport.textInputAutocapitalization(_:)`` view modifier. + public struct TextInputAutocapitalization: Equatable { internal let capitalization: UITextAutocapitalizationType fileprivate init(capitalization: UITextAutocapitalizationType) { @@ -38,27 +80,41 @@ extension Backport { /// Defines an autocapitalizing behavior that will capitalize every letter. public static var characters: TextInputAutocapitalization { .init(capitalization: .allCharacters) } + /// Creates a new `Backport.TextInputAutocapitalization` struct from a + /// `UITextAutocapitalizationType` enum. public init?(_ type: UITextAutocapitalizationType) { self.capitalization = type } } } +@available(iOS, deprecated: 15) private struct AutoCapitalizationModifier: ViewModifier { let capitalization: UITextAutocapitalizationType func body(content: Content) -> some View { content .inspect { inspector in - inspector.ancestor(ofType: UITextField.self) + inspector.any(ofType: UITextField.self) } customize: { view in view.autocapitalizationType = capitalization } .inspect { inspector in - inspector.ancestor(ofType: UITextView.self) + inspector.any(ofType: UITextView.self) } customize: { view in view.autocapitalizationType = capitalization } } } + +private struct AutoCapitalizationEnvironmentKey: EnvironmentKey { + static var defaultValue: Backport.TextInputAutocapitalization? = .sentences +} + +internal extension EnvironmentValues { + var textInputAutocapitalization: Backport.TextInputAutocapitalization? { + get { self[AutoCapitalizationEnvironmentKey.self] } + set { self[AutoCapitalizationEnvironmentKey.self] = newValue } + } +} #endif diff --git a/Sources/SwiftUIBackports/iOS/Submit/Submit.swift b/Sources/SwiftUIBackports/iOS/Submit/Submit.swift index 289f7997..8b351c0f 100644 --- a/Sources/SwiftUIBackports/iOS/Submit/Submit.swift +++ b/Sources/SwiftUIBackports/iOS/Submit/Submit.swift @@ -9,7 +9,7 @@ public extension Backport where Wrapped: View { /// Different views may have different triggers for the provided action. A TextField, or SecureField will trigger this action when the user hits the hardware or software return key. This modifier may also bind this action to a default action keyboard shortcut. You may set this action on an individual view or an entire view hierarchy. /// /// TextField("Username", text: $username) - /// .onSubmit { + /// .backport.onSubmit { /// guard viewModel.validate() else { return } /// viewModel.login() /// } @@ -31,7 +31,7 @@ public extension Backport where Wrapped: View { /// A semantic label describing the label of submission within a view hierarchy. /// /// A submit label is a description of a submission action provided to a - /// view hierarchy using the ``View/onSubmit(of:_:)`` modifier. + /// view hierarchy using the ``View/backport.onSubmit(of:_:)`` modifier. @ViewBuilder func submitLabel(_ label: Backport.SubmitLabel) -> some View { Group { diff --git a/Sources/SwiftUIBackports/iOS/TextEditor/TextEditor.swift b/Sources/SwiftUIBackports/iOS/TextEditor/TextEditor.swift index 3acfa61a..feb5e3dd 100644 --- a/Sources/SwiftUIBackports/iOS/TextEditor/TextEditor.swift +++ b/Sources/SwiftUIBackports/iOS/TextEditor/TextEditor.swift @@ -18,7 +18,7 @@ extension Backport where Wrapped == Any { /// @State private var fullText: String = "This is some editable text..." /// /// var body: some View { - /// TextEditor(text: $fullText) + /// Backport.TextEditor(text: $fullText) /// .foregroundColor(Color.gray) /// .font(.custom("HelveticaNeue", size: 13)) /// .lineSpacing(5) @@ -42,7 +42,7 @@ extension Backport where Wrapped == Any { /// @State private var fullText: String = "This is some editable text..." /// /// var body: some View { - /// TextEditor(text: $fullText) + /// Backport.TextEditor(text: $fullText) /// .foregroundColor(Color.gray) /// .font(.custom("HelveticaNeue", size: 13)) /// .lineSpacing(5) @@ -100,6 +100,7 @@ extension Backport where Wrapped == Any { view.backgroundColor = .clear view.dataDetectorTypes = [] view.returnKeyType = parent.environment.backportSubmitLabel.returnKeyType + view.autocapitalizationType = parent.environment.textInputAutocapitalization?.capitalization ?? .sentences switch parent.environment.autocorrectionDisabled { case true: