草庐IT

swift - 如何为 UIPageViewControllerDataSource 提供默认实现?

coder 2023-09-13 原文

我假设这个问题的答案将解决 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/

有关swift - 如何为 UIPageViewControllerDataSource 提供默认实现?的更多相关文章

  1. ruby - 如何为 emacs 安装 ruby​​-mode - 2

    我刚刚为fedora安装了emacs。我想用emacs编写ruby。为ruby​​提供代码提示、代码完成类型功能所需的工具、扩展是什么? 最佳答案 ruby-mode已经包含在Emacs23之后的版本中。不过,它也可以通过ELPA获得。您可能感兴趣的其他一些事情是集成RVM、feature-mode(Cucumber)、rspec-mode、ruby-electric、inf-ruby、rinari(用于Rails)等。这是我当前用于Ruby开发的Emacs配置:https://github.com/citizen428/emacs

  2. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  3. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  4. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  8. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. ruby-on-rails - 如何在 Rails 中设置路由的默认格式? - 2

    路由有如下代码:resources:orders,only:[:create],defaults:{format:'json'}resources:users,only:[:create,:update],defaults:{format:'json'}resources:delivery_types,only:[:index],defaults:{format:'json'}resources:time_corrections,only:[:index],defaults:{format:'json'}是否可以使用1个字符串为所有资源设置默认格式,每行不带“默认值”散列?谢谢。

随机推荐