Swift:继承 UIViewController 的根视图

Posted

技术标签:

【中文标题】Swift:继承 UIViewController 的根视图【英文标题】:Swift: Subclassing a UIViewController's root view 【发布时间】:2014-06-10 01:47:56 【问题描述】:

tl;dr:我如何告诉 Swift 我正在用 UIView 的子类覆盖 MyViewControllerview 属性?


我想做的事

我非常喜欢为UIViewController 的视图提供UIView 的子类。例如:

// MyView --------------------------------------------------------

@interface MyView: UIView
@property (nonatomic, strong) UITableView *tableView;    
@end

@implementation MyView

- (instancetype)initWithFrame:(CGRect)frame 
    if (self = [super initWithFrame:frame]) 
        _tableView = [[UITableView alloc] initWithFrame:frame];
    
    return self;


@end

// MyViewController ----------------------------------------------

@interface MyViewController: UIViewController <UITableViewDataSource>
@property (nonatomic, retain) MyView *view;
@end

@implementation MyViewController

- (void)loadView 
    self.view = [[MyView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];


- (void)viewDidLoad 
    [super viewDidLoad];

    self.view.tableView.dataSource = self;
    // etc.


@end

这很棒,因为它将视图创建和布局逻辑与视图控制器分开。好的,好的。

据我所知,这翻译成 Swift 是这样的:

// MyView --------------------------------------------------------

class MyView: UIView 
    let tableView: UITableView!

    init(frame: CGRect) 
        super.init(frame: frame)
        tableView = UITableView(frame: frame)
    


// MyViewController ----------------------------------------------

class MyViewController: UIViewController, UITableViewDataSource 
    override func loadView() 
        view = MyView(frame: UIScreen.mainScreen().bounds)
    

    override func viewDidLoad() 
        super.viewDidLoad()

        // this causes the compiler to complain with:
        // 'UIView' does not have a member named 'tableView'
        self.view.tableView.dataSource = self
    

问题是我似乎不知道如何告诉视图控制器它的viewMyView 的一个实例而不是UIView 本身。

尝试失败

这是我迄今为止尝试过的:

我在MyViewController 的顶部试过这个,但出现以下错误:

override var view: MyView!
// error: Cannot override mutable property 'view' of
//        type 'UIView' with covariant type 'MyView!'

我在loadView 中尝试过这个,但没有运气:

view = MyView(frame: UIScreen.mainScreen().bounds) as MyView
// this produces the same error as in the original code:
// 'UIView' does not have a member named 'tableView'

那么问题来了

如何告诉 Swift 我正在使用子类 MyView 之一覆盖 MyViewControllerview 属性?这甚至可能吗?如果没有,为什么不呢?

【问题讨论】:

【参考方案1】:

我认为完全相同的实现在 Swift 中可能是不可能的。来自关于覆盖属性的 Swift 书籍部分:

“您必须始终说明您购买的房产的名称和类型 正在覆盖,以使编译器能够检查您的覆盖 匹配具有相同名称和类型的超类属性。”

但是,您可以使用返回视图控制器 view 属性的类型转换版本的计算属性,它几乎一样干净:

class MyViewController: UIViewController, UITableViewDataSource 
    var myView: MyView!  return self.view as MyView 

    override func loadView() 
        view = MyView(frame: UIScreen.mainScreen().bounds)
    

    override func viewDidLoad() 
        super.viewDidLoad()
        self.myView.tableView.dataSource = self
    

【讨论】:

很遗憾没有办法改变view类。如果我们创建这个控制器的另一个子类怎么办?按照这种模式,我们应该创建类似myMyView...的唯一解决方案是编写getter func view() -&gt; MyView,当我们需要再次子类化时,我们可以编写override func view() -&gt; MyAnotherView。缺点是我们使用self.view() 来查看我们的视图。 这只是给了我一个黑色的视图...似乎不起作用。 Apple 甚至在UITableViewController 中使用了类似的方法。该控制器有一个属性tableView,它返回与控制器的view 属性完全相同的视图,只是转换为UITableView【参考方案2】:

我们可以使用这样的东西吗?

class MyViewController: UIViewController, UIScrollViewDelegate 

weak var viewAlias: UIScrollView!

override func loadView() 
    let scrollView = UIScrollView(frame: UIScreen.mainScreen().bounds)
    viewAlias = scrollView

    view = scrollView


override func viewDidLoad() 
    //super.viewDidLoad()
    viewAlias.delegate = self
    viewAlias.contentSize = view.frame.size

    // Do any additional setup after loading the view.


【讨论】:

以上是关于Swift:继承 UIViewController 的根视图的主要内容,如果未能解决你的问题,请参考以下文章

为许多 UIViewController 继承背景颜色和其他属性

swift 4继承了啥

带有 Swift 5.0 编译器的 Xcode 10.2 - 协议继承问题

如何在 Swift 5 中创建一个继承自另一个类的 UIImageView 类

在 UITableViewController 上使用 UIViewController 类

如何在 Swift 中对私有或内部函数进行单元测试?