我假设这个问题的答案将解决 Objective-C 协议(protocol)的一般问题,但这是我遇到的第一个此类问题。
我希望在实现 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)的协议(protocol),作为将该协议(protocol)要求的默认实现注入(inject)最终采用类的一种方式?是的;此代码是合法的:
protocol Speaker {
func speak()
}
protocol DefaultSpeaker : Speaker {
}
extension DefaultSpeaker {
func speak() {
print("howdy")
}
}
class Adopter : DefaultSpeaker {
}
好的,那么您的代码还做了什么?好吧,它还注入(inject)了一个额外的要求(实例变量)。那是合法的吗?是的。此代码也是合法的:
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 不喜欢什么?我们在这里没有做什么,而您的代码做了什么?事实上,原始协议(protocol)是@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 对协议(protocol)扩展一无所知。由于我们对所需协议(protocol)方法的实现取决于协议(protocol)扩展,因此我们无法以一种让编译器满意的方式采用协议(protocol),即从 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()
}
}
因此,我得出的结论是您的解决方案已经很好了。
你所做的实际上更像是这样,我们在采用者类上使用扩展来注入(inject)“钩子(Hook)”方法:
@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 可以理解。
关于swift - 如何为 UIPageViewControllerDataSource 提供默认实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31663560/