web-dev-qa-db-fra.com

Swift: Sous-classer la vue racine d'un UIViewController

tl; dr: Comment puis-je dire à Swift que je remplace la propriété MyViewController de view avec une sous-classe de UIView?


Ce que j'aimerais faire

Je suis un grand fan de fournir une sous-classe de UIView pour une vue de UIViewController. Par exemple:

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

Cela est formidable car il sépare la logique de création de vue et de présentation du contrôleur de vue. OK bien.

Aussi proche que je puisse comprendre, cela se traduit en Swift comme suit:

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

Le problème est que je n'arrive pas à comprendre comment dire au contrôleur de vue que sa view est une instance de MyView plutôt que UIView elle-même.

Tentatives infructueuses

Voici ce que j'ai essayé jusqu'à présent:

J'ai essayé ceci en haut de MyViewController, avec l'erreur suivante:

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

J'ai essayé ceci dans loadView, mais pas de chance:

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'

Alors voici la question

Comment puis-je dire à Swift que je remplace la propriété MyViewController de view par l'une des sous-classes MyView? Est-ce seulement possible? Si non pourquoi pas

21
Alex Basson

Je pense que la même implémentation exacte n’est peut-être pas possible dans Swift. Dans la section du livre Swift sur les propriétés prioritaires:

“Vous devez toujours indiquer à la fois le nom et le type de la propriété que vous avez écrasent, pour permettre au compilateur de vérifier que votre remplacement correspond à une propriété de superclasse portant le même nom et le même type. ”

Cependant, vous pouvez utiliser une propriété calculée qui retourne une version de conversion de la propriété view de votre contrôleur de vue. Elle serait presque aussi propre:

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
    }
}
23
Nate Cook

pouvons-nous utiliser quelque chose comme ça?

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.

}
0
RK1979

[Développeur Apple] le fait de cette façon 1 :

Ce qui suit montre comment vous pouvez remplacer le .__ d’un contrôleur de vue. viewDidLoad () pour convertir sa vue en SKView:

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

Sinon, le nouveau modèle de jeu de projet de Xcode le fait de cette façon:

override func viewDidLoad() {
    super.viewDidLoad()

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