Adds backports for onSubmit and submitLabel
This commit is contained in:
parent
b092912f24
commit
e4d699b678
|
@ -20,9 +20,6 @@ struct EnvironmentOutputModifier: ViewModifier {
|
|||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.onAppear {
|
||||
print(environment.debugDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
extension View {
|
||||
|
|
|
@ -38,7 +38,7 @@ internal extension PlatformView {
|
|||
for subview in views.reversed() {
|
||||
if let typed = subview as? ViewType {
|
||||
return typed
|
||||
} else if let typed = subview.child(ofType: type) {
|
||||
} else if let typed = subview.descendent(ofType: type) {
|
||||
return typed
|
||||
}
|
||||
}
|
||||
|
@ -46,11 +46,11 @@ internal extension PlatformView {
|
|||
return nil
|
||||
}
|
||||
|
||||
func child<ViewType: PlatformView>(ofType type: ViewType.Type) -> ViewType? {
|
||||
func descendent<ViewType: PlatformView>(ofType type: ViewType.Type) -> ViewType? {
|
||||
for subview in subviews {
|
||||
if let typed = subview as? ViewType {
|
||||
return typed
|
||||
} else if let typed = subview.child(ofType: type) {
|
||||
} else if let typed = subview.descendent(ofType: type) {
|
||||
return typed
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,12 @@ internal struct Inspector {
|
|||
var sourceView: PlatformView
|
||||
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? {
|
||||
hostView.ancestor(ofType: ViewType.self)
|
||||
}
|
||||
|
@ -72,8 +78,8 @@ internal struct Inspector {
|
|||
hostView.sibling(ofType: ViewType.self)
|
||||
}
|
||||
|
||||
func child<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? {
|
||||
hostView.child(ofType: ViewType.self)
|
||||
func descendent<ViewType: PlatformView>(ofType: ViewType.Type) -> ViewType? {
|
||||
hostView.descendent(ofType: ViewType.self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ final class PreviewController<Items>: UIViewController, UIAdaptivePresentationCo
|
|||
}
|
||||
|
||||
private func presentController() {
|
||||
print("Present")
|
||||
let controller = QLPreviewController(nibName: nil, bundle: nil)
|
||||
controller.dataSource = self
|
||||
controller.delegate = self
|
||||
|
|
|
@ -53,8 +53,6 @@ final class PreviewController<Items>: NSViewController, QLPreviewPanelDataSource
|
|||
}
|
||||
|
||||
private func present() {
|
||||
print("Present")
|
||||
|
||||
NSApp.mainWindow?.nextResponder = self
|
||||
|
||||
if isVisible {
|
||||
|
@ -88,18 +86,15 @@ final class PreviewController<Items>: NSViewController, QLPreviewPanelDataSource
|
|||
}
|
||||
|
||||
override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel!) -> Bool {
|
||||
print("Accept")
|
||||
return true
|
||||
}
|
||||
|
||||
override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) {
|
||||
print("Begin")
|
||||
panel.dataSource = self
|
||||
panel.reloadData()
|
||||
}
|
||||
|
||||
override func endPreviewPanelControl(_ panel: QLPreviewPanel!) {
|
||||
print("End")
|
||||
panel.dataSource = nil
|
||||
dismiss()
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,11 +50,11 @@ extension Backport where Wrapped: View {
|
|||
#if os(iOS)
|
||||
inspector.sibling(ofType: UIScrollView.self)
|
||||
?? inspector.ancestor(ofType: UIScrollView.self)
|
||||
?? inspector.child(ofType: UIScrollView.self)
|
||||
?? inspector.descendent(ofType: UIScrollView.self)
|
||||
#elseif os(macOS)
|
||||
inspector.sibling(ofType: NSScrollView.self)
|
||||
?? inspector.ancestor(ofType: NSScrollView.self)
|
||||
?? inspector.child(ofType: NSScrollView.self)
|
||||
?? inspector.descendent(ofType: NSScrollView.self)
|
||||
#endif
|
||||
} customize: { scrollView in
|
||||
#if os(iOS)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -95,7 +95,6 @@ extension OffsetShapeStyle<SystemColorsStyle> {
|
|||
case 3: color = .quaternaryLabel
|
||||
default: color = .label
|
||||
}
|
||||
print(offset)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +214,6 @@ func resolveColorProvider(_ provider: Any) -> ColorProvider? {
|
|||
|
||||
func printMirror(_ value: Any) {
|
||||
let mirror = Mirror(reflecting: value)
|
||||
print(mirror.subjectType)
|
||||
for child in mirror.children {
|
||||
print(child)
|
||||
}
|
||||
|
|
|
@ -96,16 +96,16 @@ extension Backport where Wrapped == Any {
|
|||
|
||||
view.delegate = self
|
||||
view.adjustsFontForContentSizeCategory = true
|
||||
view.autocapitalizationType = .allCharacters
|
||||
view.autocapitalizationType = .sentences
|
||||
view.backgroundColor = .clear
|
||||
view.dataDetectorTypes = []
|
||||
view.returnKeyType = parent.environment.backportSubmitLabel.returnKeyType
|
||||
|
||||
switch parent.environment.disableAutocorrection {
|
||||
case .some(true):
|
||||
switch parent.environment.autocorrectionDisabled {
|
||||
case true:
|
||||
view.autocorrectionType = .yes
|
||||
case .some(false):
|
||||
case false:
|
||||
view.autocorrectionType = .no
|
||||
case nil:
|
||||
view.autocorrectionType = .default
|
||||
}
|
||||
|
||||
let style = NSMutableParagraphStyle()
|
||||
|
|
Loading…
Reference in New Issue