如何根据 API 级别从 Swift 中的两个不同类派生一个类?

Posted

技术标签:

【中文标题】如何根据 API 级别从 Swift 中的两个不同类派生一个类?【英文标题】:How to derive one class from two different classes in Swift based on API level? 【发布时间】:2017-07-06 13:54:29 【问题描述】:

我想使用一个带有 SwipeTableViewCell 类的库(从 UITableViewCell 派生),但它只支持 ios 9,所以我想尽可能从该类派生,但如果应用程序在 9.0 以下运行,我想从普通 UITableViewCell 类派生.

这是我能想到的最好的(不工作):

@available(iOS, obsoleted: 9.0)
class MyTableViewCell : UITableViewCell 


@available(iOS 9.0, *)
class MyTableViewCell : SwipeTableViewCell 

然后我尝试从那个类派生:

class SomeOtherTableViewCell : MyTableViewCell 

Xcode 给了我以下错误并链接到上面的两个类定义:

SomeOtherTableViewCell.swift:34:31: 'MyTableViewCell' is ambiguous for type lookup in this context
Found this candidate
Found this candidate

在 Swift 中根据 API 级别有条件地从两个不同的类派生的酷方法是什么?

【问题讨论】:

继承是在编译时评估的,而不是在运行时。 @MartinR 这很糟糕,所以你说这根本不可能? 也许我忽略了一些东西,但我假设您必须定义两个子类 MyTableViewCell 和 MySwipeTableViewCell,然后在运行时使用 if #available() 创建其中一个或另一个的实例。 是的,作为最后的手段,但我们想将它用作许多不同 TableViewCells 的基类,所以它会重复很多代码。 【参考方案1】:

不幸的是(或者幸运的是,取决于你如何看待它),从语言的角度来看,你想要做的正是 Swift 或 Objective-C 无法实现的。您使用的@available 属性是告诉编译器,只有当您编译的目标具有大于您指定的deployment target 时,该类才可用。

@MartinR 提到了#available 以及您应该采用的实现目标的一般模式。

这两个东西之所以可用并且是支持向后兼容性所必需的,是因为编译后的代码不一定包含有关运行时的所有信息。编译器/链接器在编译时使用 SDK 的标头和模块映射来检查 API、类等是否存在。但是,您的应用程序在运行时与系统框架动态链接。这种模式就是为什么您的应用不必随其使用的 SDK 的每个部分的副本一起分发的原因。

在您的情况下,您拥有 SDK9 并尝试支持 iOS 8 或更低版本的运行时。 SwipeTableViewCell 在 iOS 8 中可能不起作用,因为它使用了仅在 iOS 9 中可用的 API。所有这些都不会阻止 SwipeTableViewCell 在 iOS 8 设备上存在,它只是阻止它工作。

你真的应该评估whether you need to support iOS 8。如果您将目标的部署目标更改为 iOS 9 并将其发布到 App Store,则运行 iOS 8 的人将无法获得更新或无法安装它。

更新

在做了一些思考和研究后,我想出了一个可能的解决方案来解决你的情况。我只在依赖于 UI 故事板的环境中对其进行了测试,但我认为它可以优雅地连接您的 iOS 目标。

import UIKit

class ViewController: UITableViewController 

    @IBOutlet var staticCell: Any? // In the storyboard, tell the cell to by a "MySwipeableCell"

    override func viewDidLoad() 
        super.viewDidLoad()

        if staticCell is SwipeTableViewCell 
            print("This class was converted magically, which means we're on iOS 9 or later")
        
    


class SwipeTableViewCell: UITableViewCell 
    // Standin for your library class


class MySwipeableCell: UITableViewCell 

    override func awakeAfter(using aDecoder: NSCoder) -> Any? 
        if #available(iOS 9, *) 
            // This will replace the MySwipeableCell with an instance of SwipeTableViewCell instead
            return SwipeTableViewCell.init(coder: aDecoder)
        

        return self
    

【讨论】:

@MartinR 和您对正在发生的事情提供了很好的意见,尤其是关于 doesn't prevent SwipeTableViewCell from existing on iOS 8 的部分现在让我很感兴趣!我们将不得不修改使其可运行所需的注释数量,但看起来是一种方法.. 活跃的 iOS 8 用户(为社区做出贡献)与 Fancy Swipe Menu 目前是 1:0,所以值得得到一点脑损伤。 :-) 值得一提的是,在 Objective-c 中,您尝试做的事情实际上非常简单 :-)

以上是关于如何根据 API 级别从 Swift 中的两个不同类派生一个类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 swift 3 中为 UITable 视图提供单元格缩进级别?

从 Google Maps API 中的单个 LatLong 确定缩放级别

如何根据 iOS 中的两个条件从 AppDelegate 呈现两个不同的 ViewController?

iOS Swift:如何根据特定属性查找不同类型数组的唯一成员

如何在 Swift 3 中的 mapview 上生成不同的图钉?

蓝牙低能耗清单