iOS Development

Modify Swift UI view with out utilizing iOS 17 modifier

Spread the love


Whats up I’ve some code under that creates a customized navigation stack that permits full display swipes to navigation away from the view. I need to assist iOS 17 with this practice Navigation stack however among the on change modifiers are just for iOS 17. How can I modify .onChange(of: isEnabled, preliminary: true) { oldValue, newValue in which is inside fileprivate struct FullSwipeModifier: ViewModifier { to work with iOS16 and have the identical performance? I attempted utilizing an onRecieve modifier as a substitute however I could not get that to work.

Moreover the place could be the most effective spot to execute a operate when a view is swiped away. I do not need the operate to execute mid swipe however relatively on the finish of the swipe when the view has been popped from the navigation stack. I can positively use a onDisappear however I might relatively not for my particular case.

import SwiftUI

struct ContentView: View {
    @State personal var isEnabled: Bool = false
    var physique: some View {
        FullSwipeNavigationStack {
             NavigationLink("Main Swipe View") {
                 Textual content("hiya").enableFullSwipePop(isEnabled)
             }
        }
    }
}
struct FullSwipeNavigationStack<Content material: View>: View {
    @ViewBuilder var content material: Content material
    /// Full Swipe Customized Gesture
    @State personal var customGesture: UIPanGestureRecognizer = {
        let gesture = UIPanGestureRecognizer()
        gesture.title = UUID().uuidString
        gesture.isEnabled = false
        return gesture
    }()
    var physique: some View {
        NavigationStack {
            content material
                .background {
                    AttachGestureView(gesture: $customGesture)
                }
        }
        .surroundings(.popGestureID, customGesture.title)
        .onReceive(NotificationCenter.default.writer(for: .init(customGesture.title ?? "")), carry out: { information in
            if let userInfo = information.userInfo, let standing = userInfo["status"] as? Bool {
                customGesture.isEnabled = standing
            }
        })
    }
}

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

/// Customized Setting Key for Passing Gesture ID to it is 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
    @Setting(.popGestureID) personal var gestureID
    func physique(content material: Content material) -> some View {
        content material
            .onChange(of: isEnabled, preliminary: true) { oldValue, newValue in
                guard let gestureID = gestureID else { return }
                NotificationCenter.default.put up(title: .init(gestureID), object: nil, userInfo: [
                    "status": newValue
                ])
            }
            .onDisappear(carry out: {
                guard let gestureID = gestureID else { return }
                NotificationCenter.default.put up(title: .init(gestureID), object: nil, userInfo: [
                    "status": false
                ])
            })
    }
}

/// Helper Information
fileprivate struct AttachGestureView: UIViewRepresentable {
    @Binding var gesture: UIPanGestureRecognizer
    func makeUIView(context: Context) -> UIView {
        return UIView()
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        DispatchQueue.primary.asyncAfter(deadline: .now() + 0.02) {
            /// Discovering Father or mother 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(the place: { $0.title == gesture.title }) {
                        print("Already Connected")
                    } else {
                        navigationController.addFullSwipeGesture(gesture)
                        print("Connected")
                    }
                }
            }
        }
    }
}

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

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

Leave a Reply

Your email address will not be published. Required fields are marked *