你如何为UIPageViewControllerDataSource提供默认实现?



我认为这个问题的答案将解决一般的Objective-C协议问题,但这是我遇到的第一个此类问题。

我期望在实现UIPageViewControllerDataSourceWithConnections时使用这些方法。

import UIKit
protocol UIPageViewControllerDataSourceWithConnections: UIPageViewControllerDataSource {
    var connectedViewControllers: [UIViewController] {get}
}
extension UIPageViewControllerDataSourceWithConnections {
    func pageViewController(pageViewController: UIPageViewController,
        viewControllerBeforeViewController viewController: UIViewController
    ) -> UIViewController? {return connectedViewController(
        current: viewController,
        adjustIndex: -
    )}
    func pageViewController(pageViewController: UIPageViewController,
        viewControllerAfterViewController viewController: UIViewController
    ) -> UIViewController? {return connectedViewController(
        current: viewController,
        adjustIndex: +
    )}
    private func connectedViewController(
        current viewController: UIViewController,
        adjustIndex: (Int, Int) -> Int
    ) -> UIViewController? {
        let requestedIndex = adjustIndex(connectedViewControllers.indexOf(viewController)!, 1)
        return connectedViewControllers.indices.contains(requestedIndex) ?
            connectedViewControllers[requestedIndex] : nil
    }
    func presentationCountForPageViewController(pageViewController: UIPageViewController)
    -> Int {return connectedViewControllers.count}
    func presentationIndexForPageViewController(pageViewController: UIPageViewController)
    -> Int {
        return connectedViewControllers.indexOf(pageViewController.viewControllers!.first!)!
    }
}

但是,这不会编译。我必须实现这些无意义的东西使事情工作。你能告诉我为什么吗?是否有简化代码的解决方案?

// connectedViewControllers is defined elsewhere in InstructionsPageViewController.
extension InstructionsPageViewController: UIPageViewControllerDataSourceWithConnections {
    // (self as UIPageViewControllerDataSourceWithConnections) doesn't work.
    // Workaround: use a different method name in the protocol
    func pageViewController(pageViewController: UIPageViewController,
        viewControllerBeforeViewController viewController: UIViewController
    ) -> UIViewController? {
        return 😾pageViewController(pageViewController,
            viewControllerBeforeViewController: viewController
        )
    }
    func pageViewController(pageViewController: UIPageViewController,
        viewControllerAfterViewController viewController: UIViewController
    ) -> UIViewController? {
        return 😾pageViewController(pageViewController,
            viewControllerAfterViewController: viewController
        )
    }

    // (self as UIPageViewControllerDataSourceWithConnections)
    // works for the optional methods.
    func presentationCountForPageViewController(pageViewController: UIPageViewController)
    -> Int {
        return (self as UIPageViewControllerDataSourceWithConnections)
            .presentationCountForPageViewController(pageViewController)
    }
    func presentationIndexForPageViewController(pageViewController: UIPageViewController)
    -> Int {
        return (self as UIPageViewControllerDataSourceWithConnections)
            .presentationIndexForPageViewController(pageViewController)
    }
}

当你遇到这样的问题时,当你想知道Swift语言本身的局限性时,它会帮助你将问题简化为一个更简单的版本。

首先,让我们问:是否有可能扩展采用协议的协议,作为将该协议需求的默认实现注入最终采用类的方式?是的,它是;这段代码是合法的:

protocol Speaker {
    func speak()
}
protocol DefaultSpeaker : Speaker {
}
extension DefaultSpeaker {
    func speak() {
        print("howdy")
    }
}
class Adopter : DefaultSpeaker {
}

你的代码还能做什么?它还注入了一个额外的需求(实例变量)。这合法吗?是的,它是。这段代码也是合法的:

protocol Speaker {
    func speak()
}
protocol DefaultSpeaker : Speaker {
    var whatToSay : String {get}
}
extension DefaultSpeaker {
    func speak() {
        print(self.whatToSay)
    }
}
class Adopter : DefaultSpeaker {
    var whatToSay = "howdy"
}

那么Swift不喜欢的是什么呢?我们在这里没有做什么,而你的代码做了什么?原来的协议是@objc。如果我们将protocol Speaker更改为@objc protocol Speaker(并进行所有其他必要的更改),代码将停止编译:

@objc protocol Speaker {
    func speak()
}
@objc protocol DefaultSpeaker : Speaker {
    var whatToSay : String {get}
}
extension DefaultSpeaker {
    func speak() {
        print(self.whatToSay)
    }
}
class Adopter : NSObject, DefaultSpeaker { // ERROR
    var whatToSay = "howdy"
}

我猜这是因为Objective-C对协议扩展一无所知。由于我们所需要的协议方法的实现依赖于协议扩展,我们不能以一种满足编译器的方式采用协议,从Objective-C的角度来看,该需求已经得到满足。我们必须在类中实现需求,Objective-C可以看到我们的实现(这正是你的解决方案所做的):

@objc protocol Speaker {
    func speak()
}
@objc protocol DefaultSpeaker : Speaker {
    var whatToSay : String {get}
}
extension DefaultSpeaker {
    func speak2() {
        print(self.whatToSay)
    }
}
class Adopter : NSObject, DefaultSpeaker {
    var whatToSay = "howdy"
    func speak() {
        self.speak2()
    }
}

所以,我得出结论,你的解决方案是最好的。

你所做的实际上更像这样,我们在采用者类上使用扩展来注入"钩子"方法:

@objc protocol Speaker {
    func speak()
}
@objc protocol DefaultSpeaker : Speaker {
    var whatToSay : String {get}
}
extension DefaultSpeaker {
    func speak2() {
        print(self.whatToSay)
    }
}
class Adopter : NSObject {
}
extension Adopter : DefaultSpeaker {
    var whatToSay : String { return "howdy" }
    func speak() {
        self.speak2()
    }
}

这是有效的,因为最后的extension是Objective-C 可以看到的:在Objective-C类上的扩展实际上是一个类别,这是Objective-C理解的。

相关内容

  • 没有找到相关文章

最新更新