我有一些下面的代码,它创建了一个定制的导航堆栈,使全屏滑动可以导航到远离视图的地方.我想用这个定制的导航堆栈支持iOS 17,但一些更改修饰符只适用于iOS 17.

我如何修改fileprivate struct FullSwipeModifier: ViewModifier {中的.onChange(of: isEnabled, initial: true) { oldValue, newValue in,使其与iOS16一起工作,并具有相同的功能?我试着用onReceive修改器代替,但我不能让它工作.

此外,当视图被划go 时,执行功能的最佳位置是哪里.我不希望函数在滑动过程中执行,而是在滑动结束时,当视图从导航堆栈中弹出时执行.我当然可以用onDisappear,但我宁愿不为我的具体情况.

import SwiftUI

struct ContentView: View {
    @State private var isEnabled: Bool = false
    var body: some View {
        FullSwipeNavigationStack {
             NavigationLink("Leading Swipe View") {
                 Text("hello").enableFullSwipePop(isEnabled)
             }
        }
    }
}
struct FullSwipeNavigationStack<Content: View>: View {
    @ViewBuilder var content: Content
    /// Full Swipe Custom Gesture
    @State private var customGesture: UIPanGestureRecognizer = {
        let gesture = UIPanGestureRecognizer()
        gesture.name = UUID().uuidString
        gesture.isEnabled = false
        return gesture
    }()
    var body: some View {
        NavigationStack {
            content
                .background {
                    AttachGestureView(gesture: $customGesture)
                }
        }
        .environment(\.popGestureID, customGesture.name)
        .onReceive(NotificationCenter.default.publisher(for: .init(customGesture.name ?? "")), perform: { info in
            if let userInfo = info.userInfo, let status = userInfo["status"] as? Bool {
                customGesture.isEnabled = status
            }
        })
    }
}

extension View {
    @ViewBuilder
    func enableFullSwipePop(_ isEnabled: Bool) -> some View {
        self
            .modifier(FullSwipeModifier(isEnabled: isEnabled))
    }
}

/// Custom Environment Key for Passing Gesture ID to it's subviews
fileprivate struct PopNotificationID: EnvironmentKey {
    static var defaultValue: String?
}

fileprivate extension EnvironmentValues {
    var popGestureID: String? {
        get {
            self[PopNotificationID.self]
        }
        
        set {
            self[PopNotificationID.self] = newValue
        }
    }
}

/// Helper View Modifier
fileprivate struct FullSwipeModifier: ViewModifier {
    var isEnabled: Bool
    /// Gesture ID
    @Environment(\.popGestureID) private var gestureID
    func body(content: Content) -> some View {
        content
            .onChange(of: isEnabled, initial: true) { oldValue, newValue in
                guard let gestureID = gestureID else { return }
                NotificationCenter.default.post(name: .init(gestureID), object: nil, userInfo: [
                    "status": newValue
                ])
            }
            .onDisappear(perform: {
                guard let gestureID = gestureID else { return }
                NotificationCenter.default.post(name: .init(gestureID), object: nil, userInfo: [
                    "status": false
                ])
            })
    }
}

/// Helper Files
fileprivate struct AttachGestureView: UIViewRepresentable {
    @Binding var gesture: UIPanGestureRecognizer
    func makeUIView(context: Context) -> UIView {
        return UIView()
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
            /// Finding Parent Controller
            if let parentViewController = uiView.parentViewController {
                if let navigationController = parentViewController.navigationController {
                    /// Checking if already the gesture has been added to the controller
                    if let _ = navigationController.view.gestureRecognizers?.first(where: { $0.name == gesture.name }) {
                        print("Already Attached")
                    } else {
                        navigationController.addFullSwipeGesture(gesture)
                        print("Attached")
                    }
                }
            }
        }
    }
}

fileprivate extension UINavigationController {
    /// Adding Custom FullSwipe Gesture
    /// Special thanks for this SO Answer
    /// https://stackoverflow.com/questions/20714595/extend-default-interactivepopgesturerecognizer-beyond-screen-edge
    func addFullSwipeGesture(_ gesture: UIPanGestureRecognizer) {
        guard let gestureSelector = interactivePopGestureRecognizer?.value(forKey: "targets") else { return }
        
        gesture.setValue(gestureSelector, forKey: "targets")
        view.addGestureRecognizer(gesture)
    }
}

fileprivate extension UIView {
    var parentViewController: UIViewController? {
        sequence(first: self) {
            $0.next
        }.first(where: { $0 is UIViewController}) as? UIViewController
    }
}

推荐答案

你可以try 这个解决方案,希望它能解决你的问题.

首先,为了与iOS 16兼容,将FullSwipeModifier代码替换为以下代码.

fileprivate struct FullSwipeModifier: ViewModifier {
        var isEnabled: Bool
        /// Gesture ID
        @Environment(\.popGestureID) private var gestureID
        
        func body(content: Content) -> some View {
            content
                .onAppear {
                    guard let gestureID = gestureID else { return }
                    NotificationCenter.default.post(name: .init(gestureID), object: nil, userInfo: [
                        "status": isEnabled
                    ])
                }
                .onDisappear {
                    guard let gestureID = gestureID else { return }
                    NotificationCenter.default.post(name: .init(gestureID), object: nil, userInfo: [
                        "status": false
                    ])
                }
        }
    }

 

您可以在Content View中使用.onDisappear修饰符,但有一个条件,当视图完全滑动时将调用该修饰符.

struct ContentView: View {
    @State private var isEnabled: Bool = false
    var body: some View {
        FullSwipeNavigationStack {
            NavigationLink("Leading Swipe View") {
                Text("hello")
                    .enableFullSwipePop(isEnabled)
                    .onDisappear {
                        // Check here, if the view is being dismissed due to a swipe
                        if !isEnabled {
                            // "View swiped away!"
                            print("View swiped away!")
                        }
                    }
            }
        }
    }
}

Ios相关问答推荐

在Swift5中,将某些属性值从一个类复制到另一个类实例

在SWIFT中将圆角+透明背景从导入视频(.mp4)添加到导出视频(.mov)

如何在wiftUI中管理导航栈?

如何从Windows PC在iPhone上安装Flutter 应用程序

如何防止UITest套件与Fastlane一起执行?

OBJC @selector 在另一个实现中不起作用

SwiftUI:.toolbar 不适用于 UITextView

如何在SwiftUI中实现淡入淡出加粘标题屏幕效果

CarPlay:现在播放alert 可能吗?

在 SwiftUI 中放置全局 NavigationPath 的位置

如何包含 Xcode 子项目标头?

用溢出的长文本对齐 Flutter 中的行和列

在向我的 struct 添加属性后获取线程 10:EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

通过按钮打开 AppStore

SwiftUI NavigationLink 立即加载目标视图,无需单击

警告:iPad:Icon-72.png:图标尺寸(0 x 0)

ios 崩溃 EXC_BAD_ACCESS KERN_INVALID_ADDRESS

文本未包含在 swift UI 中

为什么 Xcode 4 不创建任何产品?

iPhone:什么是 WWDR 中级证书?