Подтвердить что ты не робот

Swift: подклассификация корневого представления UIViewController

tl; dr: Как я могу сказать Swift, что я переопределяю свойство MyViewController view с подклассом UIView?


Что я хотел бы сделать

Я большой поклонник предоставления подкласса UIView для представления UIViewController. Например:

// 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
    }
}

Проблема в том, что я не могу понять, как сообщить контроллеру представления, что его view является экземпляром MyView, а не 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, что я переопределяю свойство MyViewController view с одним из подклассов MyView? Возможно ли это? Если нет, почему бы и нет?

4b9b3361

Ответ 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
    }
}

Ответ 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.

}

Ответ 3

[Разработчик Apple] делает это так 1:

Ниже показано, как вы можете переопределить контроллеры представлений viewDidLoad(), чтобы отобразить его представление в SKView:

override func viewDidLoad() {
    super.viewDidLoad()
    view = SKView()
}
var skView: SKView {
    return view as! SKView
}

В качестве альтернативы Xcode новый шаблон игры проекта делает это следующим образом:

override func viewDidLoad() {
    super.viewDidLoad()

    if let view = self.view as! SKView? {
        // Can now set up view as if it were a SKView.
    }
}