您好,我下面有一些代码创建了一个自定义导航堆栈,可以通过全屏滑动来导航离开视图。我想使用此自定义导航堆栈支持 iOS 17,但某些更改修饰符仅适用于 iOS 17。我如何修改.onChange(of: isEnabled, initial: true) { oldValue, newValue 其中位于fileprivate 结构内部FullSwipeModifier: ViewModifier {与 iOS16 一起使用并具有相同的功能?我尝试使用 onRecieve 修饰符,但无法使其工作。
此外,当视图被滑开时,哪里是执行功能的最佳位置。我不希望该函数在滑动中间执行,而是在滑动结束时执行,此时视图已从导航堆栈中弹出。我绝对可以使用 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
}
}
您可以尝试一下这个解决方案,希望它能解决您的疑问。
首先,将
FullSwipeModifier
代码替换为以下代码以兼容 iOS 16。您可以在 ContentView 中使用
.onDisappear
带有条件的修饰符,当视图完全滑动时它将被调用。