Adds backports for onSubmit and submitLabel

This commit is contained in:
Shaps Benkau 2023-05-08 13:01:23 +01:00
parent b092912f24
commit e4d699b678
10 changed files with 242 additions and 44 deletions

View File

@ -20,9 +20,6 @@ struct EnvironmentOutputModifier: ViewModifier {
func body(content: Content) -> some View { func body(content: Content) -> some View {
content content
.onAppear {
print(environment.debugDescription)
}
} }
} }
extension View { extension View {

View File

@ -38,7 +38,7 @@ internal extension PlatformView {
for subview in views.reversed() { for subview in views.reversed() {
if let typed = subview as? ViewType { if let typed = subview as? ViewType {
return typed return typed
} else if let typed = subview.child(ofType: type) { } else if let typed = subview.descendent(ofType: type) {
return typed return typed
} }
} }
@ -46,11 +46,11 @@ internal extension PlatformView {
return nil return nil
} }
func child<ViewType: PlatformView>(ofType type: ViewType.Type) -> ViewType? { func descendent<ViewType: PlatformView>(ofType type: ViewType.Type) -> ViewType? {
for subview in subviews { for subview in subviews {
if let typed = subview as? ViewType { if let typed = subview as? ViewType {
return typed return typed
} else if let typed = subview.child(ofType: type) { } else if let typed = subview.descendent(ofType: type) {
return typed return typed
} }
} }
@ -64,6 +64,12 @@ internal struct Inspector {
var sourceView: PlatformView var sourceView: PlatformView
var sourceController: PlatformViewController var sourceController: PlatformViewController
func `any`<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? {
ancestor(ofType: ViewType.self)
?? sibling(ofType: ViewType.self)
?? descendent(ofType: ViewType.self)
}
func ancestor<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? { func ancestor<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? {
hostView.ancestor(ofType: ViewType.self) hostView.ancestor(ofType: ViewType.self)
} }
@ -72,8 +78,8 @@ internal struct Inspector {
hostView.sibling(ofType: ViewType.self) hostView.sibling(ofType: ViewType.self)
} }
func child<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? { func descendent<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? {
hostView.child(ofType: ViewType.self) hostView.descendent(ofType: ViewType.self)
} }
} }

View File

@ -40,7 +40,6 @@ final class PreviewController<Items>: UIViewController, UIAdaptivePresentationCo
} }
private func presentController() { private func presentController() {
print("Present")
let controller = QLPreviewController(nibName: nil, bundle: nil) let controller = QLPreviewController(nibName: nil, bundle: nil)
controller.dataSource = self controller.dataSource = self
controller.delegate = self controller.delegate = self

View File

@ -53,8 +53,6 @@ final class PreviewController<Items>: NSViewController, QLPreviewPanelDataSource
} }
private func present() { private func present() {
print("Present")
NSApp.mainWindow?.nextResponder = self NSApp.mainWindow?.nextResponder = self
if isVisible { if isVisible {
@ -88,18 +86,15 @@ final class PreviewController<Items>: NSViewController, QLPreviewPanelDataSource
} }
override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel!) -> Bool { override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel!) -> Bool {
print("Accept")
return true return true
} }
override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) { override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) {
print("Begin")
panel.dataSource = self panel.dataSource = self
panel.reloadData() panel.reloadData()
} }
override func endPreviewPanelControl(_ panel: QLPreviewPanel!) { override func endPreviewPanelControl(_ panel: QLPreviewPanel!) {
print("End")
panel.dataSource = nil panel.dataSource = nil
dismiss() dismiss()
} }

View File

@ -1,20 +0,0 @@
#if os(iOS)
import SwiftUI
import SwiftBackports
/*
WIP
*/
@available(iOS, deprecated: 16)
private extension Backport where Wrapped: View {
func scrollContentBackground(_ visibility: Backport<Any>.Visibility) -> some View {
wrapped.inspect { inspector in
inspector.sibling(ofType: PlatformScrollView.self)
} customize: { view in
let isHidden = visibility == .hidden
print(isHidden)
}
}
}
#endif

View File

@ -0,0 +1,62 @@
import SwiftUI
import SwiftBackports
extension Backport where Wrapped: View {
func textInputAutocapitalization(_ autocapitalization: Backport<Any>.TextInputAutocapitalization?) -> some View {
wrapped.modifier(
AutoCapitalizationModifier(
capitalization: autocapitalization?.capitalization ?? .none
)
)
}
}
@available(iOS, introduced: 13, deprecated: 15)
@available(macOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
extension Backport<Any> {
public struct TextInputAutocapitalization {
internal let capitalization: UITextAutocapitalizationType
fileprivate init(capitalization: UITextAutocapitalizationType) {
self.capitalization = capitalization
}
/// Defines an autocapitalizing behavior that will not capitalize anything.
public static var never: TextInputAutocapitalization { .init(capitalization: .none) }
/// Defines an autocapitalizing behavior that will capitalize the first
/// letter of every word.
public static var words: TextInputAutocapitalization { .init(capitalization: .words) }
/// Defines an autocapitalizing behavior that will capitalize the first
/// letter in every sentence.
public static var sentences: TextInputAutocapitalization { .init(capitalization: .sentences) }
/// Defines an autocapitalizing behavior that will capitalize every letter.
public static var characters: TextInputAutocapitalization { .init(capitalization: .allCharacters) }
public init?(_ type: UITextAutocapitalizationType) {
self.capitalization = type
}
}
}
private struct AutoCapitalizationModifier: ViewModifier {
let capitalization: UITextAutocapitalizationType
func body(content: Content) -> some View {
content
.inspect { inspector in
inspector.ancestor(ofType: UITextField.self)
} customize: { view in
view.autocapitalizationType = capitalization
}
.inspect { inspector in
inspector.ancestor(ofType: UITextView.self)
} customize: { view in
view.autocapitalizationType = capitalization
}
}
}

View File

@ -50,11 +50,11 @@ extension Backport where Wrapped: View {
#if os(iOS) #if os(iOS)
inspector.sibling(ofType: UIScrollView.self) inspector.sibling(ofType: UIScrollView.self)
?? inspector.ancestor(ofType: UIScrollView.self) ?? inspector.ancestor(ofType: UIScrollView.self)
?? inspector.child(ofType: UIScrollView.self) ?? inspector.descendent(ofType: UIScrollView.self)
#elseif os(macOS) #elseif os(macOS)
inspector.sibling(ofType: NSScrollView.self) inspector.sibling(ofType: NSScrollView.self)
?? inspector.ancestor(ofType: NSScrollView.self) ?? inspector.ancestor(ofType: NSScrollView.self)
?? inspector.child(ofType: NSScrollView.self) ?? inspector.descendent(ofType: NSScrollView.self)
#endif #endif
} customize: { scrollView in } customize: { scrollView in
#if os(iOS) #if os(iOS)

View File

@ -0,0 +1,161 @@
import SwiftUI
import SwiftBackports
public extension Backport where Wrapped: View {
/// Adds an action to perform when the user submits a value to this 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 {
/// guard viewModel.validate() else { return }
/// viewModel.login()
/// }
///
@ViewBuilder
func onSubmit(_ action: @escaping () -> Void) -> some View {
Group {
if #available(iOS 15, *) {
wrapped
.onSubmit(action)
} else {
wrapped
.modifier(SubmitModifier())
.environment(\.backportSubmit, .init(submit: action))
}
}
}
/// 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.
@ViewBuilder
func submitLabel(_ label: Backport<Any>.SubmitLabel) -> some View {
Group {
if #available(iOS 15, *) {
wrapped
.submitLabel(.init(label))
} else {
wrapped
}
}
.modifier(SubmitModifier())
.environment(\.backportSubmitLabel, label)
}
}
public extension Backport where Wrapped == Any {
/// 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.
struct SubmitLabel: Equatable {
internal let returnKeyType: UIReturnKeyType
fileprivate init(_ type: UIReturnKeyType) {
returnKeyType = type
}
}
}
@available(iOS 15, *)
private extension SwiftUI.SubmitLabel {
init(_ label: Backport<Any>.SubmitLabel) {
switch label {
case .continue: self = .continue
case .done: self = .done
case .go: self = .go
case .join: self = .join
case .next: self = .next
case .return: self = .return
case .route: self = .route
case .search: self = .search
case .send: self = .send
default: self = .return
}
}
}
public extension Backport.SubmitLabel {
/// Defines a submit label with text of "Continue".
static var `continue`: Self { .init(.continue) }
/// Defines a submit label with text of "Done".
static var done: Self { .init(.done) }
/// Defines a submit label with text of "Go".
static var go: Self { .init(.go) }
/// Defines a submit label with text of "Join".
static var join: Self { .init(.join) }
/// Defines a submit label with text of "Next".
static var next: Self { .init(.next) }
/// Defines a submit label with text of "Return".
static var `return`: Self { .init(.default) }
/// Defines a submit label with text of "Route".
static var route: Self { .init(.route) }
/// Defines a submit label with text of "Search".
static var search: Self { .init(.search) }
/// Defines a submit label with text of "Send".
static var send: Self { .init(.send) }
}
internal struct SubmitAction {
let submit: () -> Void
func callAsFunction() { submit() }
}
private struct SubmitEnvironmentKey: EnvironmentKey {
static var defaultValue: SubmitAction = .init(submit: { })
}
internal extension EnvironmentValues {
var backportSubmit: SubmitAction {
get { self[SubmitEnvironmentKey.self] }
set { self[SubmitEnvironmentKey.self] = newValue }
}
}
private struct SubmitLabelEnvironmentKey: EnvironmentKey {
static var defaultValue: Backport.SubmitLabel = .return
}
internal extension EnvironmentValues {
var backportSubmitLabel: Backport<Any>.SubmitLabel {
get { self[SubmitLabelEnvironmentKey.self] }
set { self[SubmitLabelEnvironmentKey.self] = newValue }
}
}
private struct SubmitModifier: ViewModifier {
@Environment(\.backportSubmit) private var submit
@Environment(\.backportSubmitLabel) private var label
@Backport.StateObject private var coordinator: Coordinator = .init()
func body(content: Content) -> some View {
content
.inspect { inspector in
inspector.any(ofType: UITextView.self)
} customize: { view in
view.returnKeyType = label.returnKeyType
}
.inspect { inspector in
inspector.any(ofType: UITextField.self)
} customize: { view in
view.returnKeyType = label.returnKeyType
coordinator.onReturn = { submit() }
coordinator.observe(view: view)
}
}
final class Coordinator: NSObject, ObservableObject {
private(set) weak var field: UITextField?
var onReturn: () -> Void = { }
@objc private func didReturn() { onReturn() }
override init() { }
func observe(view: UITextField) {
view.addTarget(self, action: #selector(didReturn), for: .editingDidEndOnExit)
}
}
}

View File

@ -95,7 +95,6 @@ extension OffsetShapeStyle<SystemColorsStyle> {
case 3: color = .quaternaryLabel case 3: color = .quaternaryLabel
default: color = .label default: color = .label
} }
print(offset)
} }
} }
@ -215,7 +214,6 @@ func resolveColorProvider(_ provider: Any) -> ColorProvider? {
func printMirror(_ value: Any) { func printMirror(_ value: Any) {
let mirror = Mirror(reflecting: value) let mirror = Mirror(reflecting: value)
print(mirror.subjectType)
for child in mirror.children { for child in mirror.children {
print(child) print(child)
} }

View File

@ -96,16 +96,16 @@ extension Backport where Wrapped == Any {
view.delegate = self view.delegate = self
view.adjustsFontForContentSizeCategory = true view.adjustsFontForContentSizeCategory = true
view.autocapitalizationType = .allCharacters view.autocapitalizationType = .sentences
view.backgroundColor = .clear view.backgroundColor = .clear
view.dataDetectorTypes = []
view.returnKeyType = parent.environment.backportSubmitLabel.returnKeyType
switch parent.environment.disableAutocorrection { switch parent.environment.autocorrectionDisabled {
case .some(true): case true:
view.autocorrectionType = .yes view.autocorrectionType = .yes
case .some(false): case false:
view.autocorrectionType = .no view.autocorrectionType = .no
case nil:
view.autocorrectionType = .default
} }
let style = NSMutableParagraphStyle() let style = NSMutableParagraphStyle()