কীভাবে সুইফটইয়আই-তে সোয়াইপ অঙ্গভঙ্গিটি ইউআইকিট-এর মতো আচরণ করতে হবে (ইন্টারেক্টিভপপজেষ্টারআর সনাক্তকারী)


9

ইন্টারেক্টিভ পপ অঙ্গভঙ্গি শনাক্তকারীকে নেভিগেশন স্ট্যাকের আগের দৃশ্যটি ফিরে যেতে দেয় যখন তারা অর্ধেক স্ক্রিনের চেয়ে আরও সোয়াইপ করে (বা সেই লাইনের চারপাশে কিছু)। সোয়াইফআইআইতে যখন সোয়াইপ খুব বেশি ছিল না তখন অঙ্গভঙ্গিটি বাতিল হয় না।

সুইফটুই: https://imgur.com/xxVnhY7

ইউআইকিট: https://imgur.com/f6WBUne


প্রশ্ন:

সুইফটইউআই ভিউ ব্যবহার করার সময় কি ইউআইকিট আচরণ পাওয়া সম্ভব?


প্রচেষ্টা

আমি একটি ইউআইএনএভিগেশন কন্ট্রোলারের ভিতরে একটি ইউআইহোস্টিং কনট্রোলার এম্বেড করার চেষ্টা করেছি তবে এটি নেভিগেশনভিউয়ের মতো সঠিক আচরণ দেয়।

struct ContentView: View {
    var body: some View {
        UIKitNavigationView {
            VStack {
                NavigationLink(destination: Text("Detail")) {
                    Text("SwiftUI")
                }
            }.navigationBarTitle("SwiftUI", displayMode: .inline)
        }.edgesIgnoringSafeArea(.top)
    }
}

struct UIKitNavigationView<Content: View>: UIViewControllerRepresentable {

    var content: () -> Content

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }

    func makeUIViewController(context: Context) -> UINavigationController {
        let host = UIHostingController(rootView: content())
        let nvc = UINavigationController(rootViewController: host)
        return nvc
    }

    func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}

উত্তর:


4

আমি ডিফল্টকে ওভাররাইড করে শেষ করে দিয়েছি NavigationViewএবং NavigationLinkপছন্দসই আচরণ পেতে পারি। এটি এত সহজ বলে মনে হচ্ছে যে আমি অবশ্যই ডিফল্ট SwiftUI দর্শনগুলি এমন কিছু উপেক্ষা করব?

NavigationView

আমি UINavigationControllerএকটি সুপার সিম্পলে একটি মোড়ানো করি যা সুইফটইউআই কনটেন্ট ভিউটিকে এনভায়রনমেন্ট অবজেক্ট হিসাবে UIViewControllerRepresentableদেয় UINavigationController। এর অর্থ হ'ল NavigationLinkপরবর্তীতে এটি ধরে নিতে পারে যতক্ষণ না এটি একই নেভিগেশন নিয়ামক হিসাবে থাকে (উপস্থাপিত ভিউ কন্ট্রোলাররা এনভায়রনঅবজেক্টগুলি গ্রহণ করে না) যা আমরা চাই ঠিক তেমনই।

দ্রষ্টব্য: নেভিগেশনভিউয়ের প্রয়োজন .edgesIgnoringSafeArea(.top)এবং স্ট্রাক্টটিতে এটি কীভাবে সেট করা যায় তা আমি জানি না। যদি আপনার এনভিসি শীর্ষে কেটে যায় তবে উদাহরণটি দেখুন।

struct NavigationView<Content: View>: UIViewControllerRepresentable {

    var content: () -> Content

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }

    func makeUIViewController(context: Context) -> UINavigationController {
        let nvc = UINavigationController()
        let host = UIHostingController(rootView: content().environmentObject(nvc))
        nvc.viewControllers = [host]
        return nvc
    }

    func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}

extension UINavigationController: ObservableObject {}

NavigationLink

আমি একটি কাস্টম নেভিগেশনলিঙ্ক তৈরি করি যা ইউআইএন হোস্টিংকন্ট্রোলারকে পরবর্তী দৃশ্যের হোস্টিংয়ে ঠেলে পরিবেশগুলি ইউআইএনএভিগেশন কন্ট্রোলার অ্যাক্সেস করে।

দ্রষ্টব্য: আমি বাস্তবায়ন করিনিselectionisActive সুইফটইউআই নেভিগেশনলিঙ্কটি এবং কারণ তারা এখনও কী করে তা আমি পুরোপুরি বুঝতে পারি না। আপনি যদি এটির সাথে সহায়তা করতে চান তবে মন্তব্য / সম্পাদনা করুন।

struct NavigationLink<Destination: View, Label:View>: View {
    var destination: Destination
    var label: () -> Label

    public init(destination: Destination, @ViewBuilder label: @escaping () -> Label) {
        self.destination = destination
        self.label = label
    }

    /// If this crashes, make sure you wrapped the NavigationLink in a NavigationView
    @EnvironmentObject var nvc: UINavigationController

    var body: some View {
        Button(action: {
            let rootView = self.destination.environmentObject(self.nvc)
            let hosted = UIHostingController(rootView: rootView)
            self.nvc.pushViewController(hosted, animated: true)
        }, label: label)
    }
}

এটি পিছনে সোয়াইপগুলি সুইফটুইতে সঠিকভাবে কাজ না করার সমাধান করে এবং কারণ আমি আমার সম্পূর্ণ প্রকল্পটি নেভিগেশনভিউ এবং নেভিগেশনলিঙ্ক নামগুলি ব্যবহার করি ততক্ষনে এইগুলিতে স্যুইচ করা হয়েছে।

উদাহরণ

উদাহরণে আমি মোডাল উপস্থাপনাটিও দেখাই।

struct ContentView: View {
    @State var isPresented = false

    var body: some View {
        NavigationView {
            VStack(alignment: .center, spacing: 30) {
                NavigationLink(destination: Text("Detail"), label: {
                    Text("Show detail")
                })
                Button(action: {
                    self.isPresented.toggle()
                }, label: {
                    Text("Show modal")
                })
            }
            .navigationBarTitle("SwiftUI")
        }
        .edgesIgnoringSafeArea(.top)
        .sheet(isPresented: $isPresented) {
            Modal()
        }
    }
}
struct Modal: View {
    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        NavigationView {
            VStack(alignment: .center, spacing: 30) {
                NavigationLink(destination: Text("Detail"), label: {
                    Text("Show detail")
                })
                Button(action: {
                    self.presentationMode.wrappedValue.dismiss()
                }, label: {
                    Text("Dismiss modal")
                })
            }
            .navigationBarTitle("Modal")
        }
    }
}

সম্পাদনা: আমি "এটি এত সহজ বলে মনে হচ্ছে যে আমি অবশ্যই কিছু উপেক্ষা করব" এবং আমি মনে করি এটি খুঁজে পেয়েছি। এটি পরিবেশের বিষয়গুলি পরের দৃশ্যে স্থানান্তরিত করে বলে মনে হচ্ছে না। আমি জানি না যে ডিফল্ট নেভিগেশনলিঙ্ক কীভাবে এটি করে তাই আপাতত আমি নিজেই যেখানে প্রয়োজন সেখানে পরবর্তী দর্শনে ম্যানুয়ালি প্রেরণ করি।

NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
    Text("Show detail")
}

সম্পাদনা 2:

এটি করে নেভিগেশন কন্ট্রোলারটিকে অভ্যন্তরীণ সমস্ত দর্শনে প্রকাশিত NavigationViewকরে @EnvironmentObject var nvc: UINavigationController। এটি ঠিক করার উপায়টি পরিবেশ ফাইলকে তৈরি করা হচ্ছে যা আমরা ফাইলপ্রাইভেট শ্রেণীর নেভিগেশন পরিচালনা করতে ব্যবহার করি। আমি এটিকে সংক্ষেপে ঠিক করেছি: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb


আর্গুমেন্ট প্রকারের 'ইউআইএনএভিগেশন কন্ট্রোলার' প্রত্যাশিত ধরণের 'অবজারভেবলবজেক্ট' এর সাথে খাপ খায় না
stardust4891

@ কেজোডিওন আমি স্ট্যাকওভারফ্লো পোস্টে এটি যুক্ত করতে ভুলে গিয়েছিলাম তবে এটির সূচনাটি ছিল:extension UINavigationController: ObservableObject {}
ক্যাস্পার জ্যানডবার্গেন

এটি আমার পিছনে থাকা সোয়াইপ বাগটি ঠিক করেছে তবে দুর্ভাগ্যক্রমে এটি অনুরোধগুলি আনার জন্য পরিবর্তনগুলি স্বীকার করে না এবং ডিফল্ট নেভিগেশনভিউ কীভাবে তা করে না বলে মনে হয়।
stardust4891

@ কেজোডিওন আহ খুব খারাপ, আমি জানি যে সমাধানের সাথে পরিবেশের বিষয়গুলি রয়েছে। আপনি কী অনুরোধ করছেন তা নিশ্চিত নয়। হতে পারে একটি নতুন প্রশ্ন খুলুন।
ক্যাস্পার জ্যানডবার্গেন

ভাল আমার কাছে বেশ কয়েকটি আনার অনুরোধ রয়েছে যা আমি পরিচালিত অবজেক্ট প্রসঙ্গটি সংরক্ষণ করার পরে স্বয়ংক্রিয়ভাবে ইউআইতে আপডেট হয়ে যায়। যখন আমি আপনার কোডটি প্রয়োগ করি তখন কোনও কারণে তারা কাজ করে না। আমি সত্যিই তাদের ইচ্ছা করি, কারণ এটি একটি পিছনে সোয়াইপ সমস্যাটি সমাধান করেছে যা আমি কয়েকদিন ধরে সমাধান করার চেষ্টা করছি।
stardust4891

1

আপনি এটি ইউআইকিটিতে নেমে এবং নিজের ইউআইএনএভিগেশন কন্ট্রোলার ব্যবহার করে করতে পারেন।

প্রথমে একটি SwipeNavigationControllerফাইল তৈরি করুন:

import UIKit
import SwiftUI

final class SwipeNavigationController: UINavigationController {

    // MARK: - Lifecycle

    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        delegate = self
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // This needs to be in here, not in init
        interactivePopGestureRecognizer?.delegate = self
    }

    deinit {
        delegate = nil
        interactivePopGestureRecognizer?.delegate = nil
    }

    // MARK: - Overrides

    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        duringPushAnimation = true

        super.pushViewController(viewController, animated: animated)
    }

    var duringPushAnimation = false

    // MARK: - Custom Functions

    func pushSwipeBackView<Content>(_ content: Content) where Content: View {
        let hostingController = SwipeBackHostingController(rootView: content)
        self.delegate = hostingController
        self.pushViewController(hostingController, animated: true)
    }

}

// MARK: - UINavigationControllerDelegate

extension SwipeNavigationController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }

        swipeNavigationController.duringPushAnimation = false
    }

}

// MARK: - UIGestureRecognizerDelegate

extension SwipeNavigationController: UIGestureRecognizerDelegate {

    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard gestureRecognizer == interactivePopGestureRecognizer else {
            return true // default value
        }

        // Disable pop gesture in two situations:
        // 1) when the pop animation is in progress
        // 2) when user swipes quickly a couple of times and animations don't have time to be performed
        let result = viewControllers.count > 1 && duringPushAnimation == false
        return result
    }
}

যোগ SwipeNavigationControllerকরার সাথে এটি এখানে একই সরবরাহ করা হয়েছেpushSwipeBackView() ফাংশন হয়।

এই ফাংশনটির একটি প্রয়োজন SwipeBackHostingControllerযা আমরা সংজ্ঞায়িত করি

import SwiftUI

class SwipeBackHostingController<Content: View>: UIHostingController<Content>, UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
        swipeNavigationController.duringPushAnimation = false
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
        swipeNavigationController.delegate = nil
    }
}

তারপরে আমরা অ্যাপ্লিকেশনটি SceneDelegateব্যবহার করতে সেট আপ করি SwipeNavigationController:

    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        let hostingController = UIHostingController(rootView: ContentView())
        window.rootViewController = SwipeNavigationController(rootViewController: hostingController)
        self.window = window
        window.makeKeyAndVisible()
    }

শেষ পর্যন্ত এটি আপনার ব্যবহার করুন ContentView:

struct ContentView: View {
    func navController() -> SwipeNavigationController {
        return UIApplication.shared.windows[0].rootViewController! as! SwipeNavigationController
    }

    var body: some View {
        VStack {
            Text("SwiftUI")
                .onTapGesture {
                    self.navController().pushSwipeBackView(Text("Detail"))
            }
        }.onAppear {
            self.navController().navigationBar.topItem?.title = "Swift UI"
        }.edgesIgnoringSafeArea(.top)
    }
}

1
আপনার কাস্টম সোয়াইপ নেভিগেশন নিয়ন্ত্রক আসলে ডিফল্ট ইউআইএনএভিগেশন কন্ট্রোলার আচরণ থেকে কিছুই পরিবর্তন করে না। func navController()ভিসি দখল করতে এবং তারপর ভিসি নিজেকে আসলে একটি মহান ধারণা ধাক্কা এবং সাহায্য করেছে আমাকে এই সমস্যার জিনিসটা! আমি আরও একটি SwiftUI বন্ধুত্বপূর্ণ উত্তর উত্তর দেব, কিন্তু আপনার সহায়তার জন্য আপনাকে ধন্যবাদ!
ক্যাস্পার জ্যান্ডবার্গেন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.