Introduces a backport for TextInputCapitalization

This commit is contained in:
Shaps Benkau 2023-05-08 13:23:35 +01:00
parent affe67f2cf
commit 5a294a710f
3 changed files with 74 additions and 17 deletions

View File

@ -2,22 +2,64 @@ import SwiftUI
import SwiftBackports import SwiftBackports
#if os(iOS) #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<Any>.TextInputAutocapitalization.sentences`.
///
/// - Parameter autocapitalization: One of the capitalizing behaviors
/// defined in the `Backport<Any>.TextInputAutocapitalization` struct or nil.
@ViewBuilder
func textInputAutocapitalization(_ autocapitalization: Backport<Any>.TextInputAutocapitalization?) -> some View { func textInputAutocapitalization(_ autocapitalization: Backport<Any>.TextInputAutocapitalization?) -> some View {
wrapped.modifier( Group {
AutoCapitalizationModifier( if #available(iOS 16, *) {
capitalization: autocapitalization?.capitalization ?? .none 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(iOS, deprecated: 15)
@available(macOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
extension Backport<Any> { extension Backport<Any> {
public struct TextInputAutocapitalization { /// The kind of autocapitalization behavior applied during text input.
///
/// Pass an instance of `Backport<Any>.TextInputAutocapitalization` to the
/// ``View/backport.textInputAutocapitalization(_:)`` view modifier.
public struct TextInputAutocapitalization: Equatable {
internal let capitalization: UITextAutocapitalizationType internal let capitalization: UITextAutocapitalizationType
fileprivate init(capitalization: UITextAutocapitalizationType) { fileprivate init(capitalization: UITextAutocapitalizationType) {
@ -38,27 +80,41 @@ extension Backport<Any> {
/// Defines an autocapitalizing behavior that will capitalize every letter. /// Defines an autocapitalizing behavior that will capitalize every letter.
public static var characters: TextInputAutocapitalization { .init(capitalization: .allCharacters) } public static var characters: TextInputAutocapitalization { .init(capitalization: .allCharacters) }
/// Creates a new `Backport<Any>.TextInputAutocapitalization` struct from a
/// `UITextAutocapitalizationType` enum.
public init?(_ type: UITextAutocapitalizationType) { public init?(_ type: UITextAutocapitalizationType) {
self.capitalization = type self.capitalization = type
} }
} }
} }
@available(iOS, deprecated: 15)
private struct AutoCapitalizationModifier: ViewModifier { private struct AutoCapitalizationModifier: ViewModifier {
let capitalization: UITextAutocapitalizationType let capitalization: UITextAutocapitalizationType
func body(content: Content) -> some View { func body(content: Content) -> some View {
content content
.inspect { inspector in .inspect { inspector in
inspector.ancestor(ofType: UITextField.self) inspector.any(ofType: UITextField.self)
} customize: { view in } customize: { view in
view.autocapitalizationType = capitalization view.autocapitalizationType = capitalization
} }
.inspect { inspector in .inspect { inspector in
inspector.ancestor(ofType: UITextView.self) inspector.any(ofType: UITextView.self)
} customize: { view in } customize: { view in
view.autocapitalizationType = capitalization view.autocapitalizationType = capitalization
} }
} }
} }
private struct AutoCapitalizationEnvironmentKey: EnvironmentKey {
static var defaultValue: Backport<Any>.TextInputAutocapitalization? = .sentences
}
internal extension EnvironmentValues {
var textInputAutocapitalization: Backport<Any>.TextInputAutocapitalization? {
get { self[AutoCapitalizationEnvironmentKey.self] }
set { self[AutoCapitalizationEnvironmentKey.self] = newValue }
}
}
#endif #endif

View File

@ -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. /// 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) /// TextField("Username", text: $username)
/// .onSubmit { /// .backport.onSubmit {
/// guard viewModel.validate() else { return } /// guard viewModel.validate() else { return }
/// viewModel.login() /// 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 semantic label describing the label of submission within a view hierarchy.
/// ///
/// A submit label is a description of a submission action provided to a /// 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 @ViewBuilder
func submitLabel(_ label: Backport<Any>.SubmitLabel) -> some View { func submitLabel(_ label: Backport<Any>.SubmitLabel) -> some View {
Group { Group {

View File

@ -18,7 +18,7 @@ extension Backport where Wrapped == Any {
/// @State private var fullText: String = "This is some editable text..." /// @State private var fullText: String = "This is some editable text..."
/// ///
/// var body: some View { /// var body: some View {
/// TextEditor(text: $fullText) /// Backport.TextEditor(text: $fullText)
/// .foregroundColor(Color.gray) /// .foregroundColor(Color.gray)
/// .font(.custom("HelveticaNeue", size: 13)) /// .font(.custom("HelveticaNeue", size: 13))
/// .lineSpacing(5) /// .lineSpacing(5)
@ -42,7 +42,7 @@ extension Backport where Wrapped == Any {
/// @State private var fullText: String = "This is some editable text..." /// @State private var fullText: String = "This is some editable text..."
/// ///
/// var body: some View { /// var body: some View {
/// TextEditor(text: $fullText) /// Backport.TextEditor(text: $fullText)
/// .foregroundColor(Color.gray) /// .foregroundColor(Color.gray)
/// .font(.custom("HelveticaNeue", size: 13)) /// .font(.custom("HelveticaNeue", size: 13))
/// .lineSpacing(5) /// .lineSpacing(5)
@ -100,6 +100,7 @@ extension Backport where Wrapped == Any {
view.backgroundColor = .clear view.backgroundColor = .clear
view.dataDetectorTypes = [] view.dataDetectorTypes = []
view.returnKeyType = parent.environment.backportSubmitLabel.returnKeyType view.returnKeyType = parent.environment.backportSubmitLabel.returnKeyType
view.autocapitalizationType = parent.environment.textInputAutocapitalization?.capitalization ?? .sentences
switch parent.environment.autocorrectionDisabled { switch parent.environment.autocorrectionDisabled {
case true: case true: