【问题标题】:Child View Controllers in Page View Controller Failing to Receive Delegate Calls页面视图控制器中的子视图控制器无法接收委托调用
【发布时间】:2021-04-19 04:04:28
【问题描述】:

我的两个子视图控制器在父 PageViewController 中遇到问题,其中一个子调用的委托没有被另一个子接收。

我的第一个孩子包含按钮,当按下按钮时,会在另一个孩子中触发委托以暂停计时器。但接听电话失败,定时器继续运行。

这是我的 PageViewController:

class StartMaplessWorkoutPageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {

    lazy var workoutViewControllers: [UIViewController] = {
        return [self.getNewViewController(viewController: "ButtonsViewController"), self.getNewViewController(viewController: "DisplayMaplessViewController")]
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.dataSource = self
        
        // Saw this from another answer, doesn't do anything that helps (at the moment)
        let buttonsViewController = storyboard?.instantiateViewController(withIdentifier: "ButtonsViewController") as! ButtonsViewController
        let displayMaplessViewController = storyboard?.instantiateViewController(withIdentifier: "DisplayMaplessViewController") as! DisplayMaplessViewController
        
        buttonsViewController.buttonsDelegate = displayMaplessViewController
        
        if let firstViewController = workoutViewControllers.last {
            setViewControllers([firstViewController], direction: .forward, animated: true, completion: nil)
        }
        
        let pageControl = UIPageControl.appearance(whenContainedInInstancesOf: [StartWorkoutPageViewController.self])
        pageControl.currentPageIndicatorTintColor = .orange
        pageControl.pageIndicatorTintColor = .gray
    }
    
    func getNewViewController(viewController: String) -> UIViewController {
        return (storyboard?.instantiateViewController(withIdentifier: viewController))!
    }
    
    // MARK: PageView DataSource
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
            return nil
        }
        
        let previousIndex = viewControllerIndex - 1
        
        guard previousIndex >= 0 else {
            return workoutViewControllers.last
        }
        
        guard workoutViewControllers.count > previousIndex else {
            return nil
        }
        
        return workoutViewControllers[previousIndex]
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
            return nil
        }
        
        let nextIndex = viewControllerIndex + 1
        let workoutViewControllersCount = workoutViewControllers.count
        
        guard workoutViewControllersCount != nextIndex else {
            return workoutViewControllers.first
        }
        
        guard workoutViewControllersCount > nextIndex else {
            return nil
        }
        
        return workoutViewControllers[nextIndex]
    }
    
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return workoutViewControllers.count
    }
    
    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        guard let firstViewController = viewControllers?.first, let firstViewControllerIndex = workoutViewControllers.firstIndex(of: firstViewController) else {
            return 0
        }
        
        return firstViewControllerIndex
    }
}

我的带按钮的 ChildViewController:

protocol ButtonsViewDelegate: class {
    func onButtonPressed(button: String)
}

class ButtonsViewController: UIViewController {
    
    weak var buttonsDelegate: ButtonsViewDelegate?
    
    var isPaused: Bool = false
    
    @IBOutlet weak var startStopButton: UIButton!
    @IBOutlet weak var optionsButton: UIButton!
    @IBOutlet weak var endButton: UIButton!
    
    @IBAction func startStopButton(_ sender: Any) {
        if isPaused == true {
            buttonsDelegate?.onButtonPressed(button: "Start")
            isPaused = false
        } else {
            buttonsDelegate?.onButtonPressed(button: "Pause")
            isPaused = true
        }
    }
    
    @IBAction func endButton(_ sender: Any) {
        let menu = UIAlertController(title: "End", message: "Are you sure you want to end?", preferredStyle: .actionSheet)
        
        let end = UIAlertAction(title: "End", style: .default, handler: { handler in
            self.buttonsDelegate?.onButtonPressed(button: "End")
        })
        
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
        menu.addAction(end)
        menu.addAction(cancelAction)
        
        self.present(menu, animated: true, completion: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

我的另一个 ChildViewController,它应该正在接收 ButtonsViewDelegate 的调用:

import UIKit

class DisplayMaplessViewController: UIViewController, ButtonsViewDelegate {
    
    var timer = Timer()
    var currentTime: TimeInterval = 0.0
    var isCountdown: Bool = false
    var isInterval: Bool = false
    var currentRepeats: Int = 0
    var currentActivity: Int = 0
    var count: Int = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        startIntervalTimer(withTime: 0)
    }

    // Currently not being called
    func onButtonPressed(button: String) {
        switch button {
        case "Start":
            restartIntervalTimer()
        case "Pause":
            pauseIntervalTimer()
        case "End":
            stop()
        default:
            break
        }
    }
    
    func startIntervalTimer(withTime: Double) {
        if withTime != 0 {
            currentTime = withTime
            if isInterval != true {
                isCountdown = true
            }
        }

        timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(intervalTimerUpdate), userInfo: nil, repeats: true)
    }
    
    func pauseIntervalTimer() {
        timer.invalidate()
    }
    
    func restartIntervalTimer() {
        timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(intervalTimerUpdate), userInfo: nil, repeats: true)
    }

    // Currently Not being called
    func stop() {
        timer.invalidate()
        let formatter = DateComponentsFormatter()
        formatter.unitsStyle = .positional
        formatter.allowedUnits = [.hour, .minute, .second]
        formatter.zeroFormattingBehavior = [.pad]
        
        let timeString = formatter.string(from: currentTime)
        
        // save the data etc

        print("Stop is called")
    }
    
    @objc func intervalTimerUpdate() {
        currentTime += 1.0
        print(currentTime)
    }

}

对不起,这太啰嗦了,尝试了一段时间,真的很生气,它不起作用!谢谢!

【问题讨论】:

    标签: ios swift delegates protocols uipageviewcontroller


    【解决方案1】:

    我会尽量说清楚,希望我会这样,因为英语不是我的母语。

    在我看来,您正在实例化要在 getNewViewController() 方法中呈现的 ViewController 并将它们存储在 workoutViewControllers 数组中,但是您将委托设置为您从未在 PageVC 中设置的单独实例。您需要使用相同的实例设置委托。

    这两个是两个 VC 两个实例(也不确定标识符“DisplayViewController”是否正确,我希望是“DisplayMaplessViewController ”,没有故事板很难说):

        let buttonsViewController = storyboard?.instantiateViewController(withIdentifier: "ButtonsViewController") as! ButtonsViewController
        let displayMaplessViewController = storyboard?.instantiateViewController(withIdentifier: "DisplayViewController") as! DisplayMaplessViewController
        
        buttonsViewController.buttonsDelegate = displayMaplessViewController
    

    数组中的这些另外两个实例,与上述无关,属于相同的两个类

        lazy var workoutViewControllers: [UIViewController] = {
        return [self.getNewViewController(viewController: "ButtonsViewController"), self.getNewViewController(viewController: "DisplayMaplessViewController")]
    }()
    

    为了更好地理解我的意思,我从头开始重构并简化了您的项目(必须以编程方式进行,因为我不习惯故事板)。 它现在由一个PageController 组成,它显示一个带有红色按钮的buttonsVC 和一个带有蓝色背景的displayMaplessVC。 按下红色按钮后,将调用委托方法,使蓝色背景变为绿色。
    看看我在做什么,因为我正在附加我设置委托的相同实例:

    1. 实例化一个 DisplayMaplessViewController 对象和 ButtonsViewController 对象;
    2. 设置buttonsVC.buttonsDelegate = displayMaplessVC;
    3. 将两个 ViewController 附加到数组中。

    这是完成它的一种方法,但肯定有其他几种方法可以达到相同的结果,一旦你明白了自己的错误,你就可以选择你最喜欢的一个。

    只需将其复制并粘贴到一个新项目中,构建并运行(您必须在 Storyboard 中将起始 ViewController 的类设置为StartMaplessWorkoutPageViewController):

    import UIKit
    
    class StartMaplessWorkoutPageViewController: UIViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {
    
    private var workoutViewControllers = [UIViewController]()
    private let pageController: UIPageViewController = {
        let pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
        return pageController
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        pageController.delegate = self
        pageController.dataSource = self
        let buttonsVC = ButtonsViewController()
        let displayMaplessVC = DisplayMaplessViewController()
        buttonsVC.buttonsDelegate = displayMaplessVC
        workoutViewControllers.append(buttonsVC)
        workoutViewControllers.append(displayMaplessVC)
        
        self.addChild(self.pageController)
        self.view.addSubview(self.pageController.view)
    
        self.pageController.setViewControllers([displayMaplessVC], direction: .forward, animated: true, completion: nil)
    
        self.pageController.didMove(toParent: self)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        pageController.view.frame = view.bounds
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        
        guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
             return nil
         }
         
         let previousIndex = viewControllerIndex - 1
         
         guard previousIndex >= 0 else {
             return workoutViewControllers.last
         }
         
         guard workoutViewControllers.count > previousIndex else {
             return nil
         }
         
         return workoutViewControllers[previousIndex]
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        
        guard let viewControllerIndex = workoutViewControllers.firstIndex(of: viewController) else {
            return nil
        }
        
        let nextIndex = viewControllerIndex + 1
        let workoutViewControllersCount = workoutViewControllers.count
        
        guard workoutViewControllersCount != nextIndex else {
            return workoutViewControllers.first
        }
        
        guard workoutViewControllersCount > nextIndex else {
            return nil
        }
        
        return workoutViewControllers[nextIndex]
    }
    
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return workoutViewControllers.count
    }
    
    }
    

    .

    protocol ButtonsViewDelegate: class {
        func onButtonPressed()
    }
    
    import UIKit
    
    class ButtonsViewController: UIViewController {
    
    weak var buttonsDelegate: ButtonsViewDelegate?
    
    let button: UIButton = {
        let button = UIButton()
        button.backgroundColor = .red
        button.addTarget(self, action: #selector(onButtonPressed), for: .touchUpInside)
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(button)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        button.frame = CGRect(x: 50,
                              y: 50,
                              width: 100,
                              height: 100)
    }
    
    @objc private func onButtonPressed() {
        buttonsDelegate?.onButtonPressed()
    }
    
    }
    

    .

    import UIKit
    
    class DisplayMaplessViewController: UIViewController, ButtonsViewDelegate {
    
    private let testView: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(testView)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        testView.frame = view.bounds
    }
    
    internal func onButtonPressed() {
        testView.backgroundColor = .green
    }
    
    }
    

    【讨论】:

    • 嗨!感谢那。我已经为您指出的错字更新了上面的代码(哎呀!)。现在开始工作了!谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多