Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Subclassing a UIViewController's root view

Tags:

tl;dr: How can I tell Swift that I'm overriding MyViewController's view property with a subclass of UIView?


What I'd like to do

I'm a big fan of providing a subclass of UIView for a UIViewController's view. For example:

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

This is great because it separates the view creation and layout logic from the view controller. Ok, fine.

Near as I can figure, this translates into Swift like so:

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

The trouble is I can't seem to figure out how to tell the view controller that its view is an instance of MyView rather than UIView itself.

Failed attempts

Here's what I've tried so far:

I've tried this at the top of MyViewController, with the following error:

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

I've tried this in loadView, but no luck:

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'

So here's the question

How can I tell Swift that I'm overriding MyViewController's view property with one of subclass MyView? Is this even possible? If not, why not?

like image 422
Alex Basson Avatar asked Jun 10 '14 01:06

Alex Basson


People also ask

How do I override a view in Swift?

final class MyViewController: UIViewController { let myView = MyView() override func viewDidLoad() { super. viewDidLoad() setupMyView() } private func setupMyView() { view. addSubview(myView) //10 lines of constraints or so myView. delegate = self //We now have both 'view' and 'myView'... } }

How do I change the root view in Swiftui?

ContentView -> View1 -> View2And from View2 you want to pop to the Root view.

What is a UIViewController subclass?

The UIViewController class defines the shared behavior that's common to all view controllers. You rarely create instances of the UIViewController class directly. Instead, you subclass UIViewController and add the methods and properties needed to manage the view controller's view hierarchy.

What is view controller in objective c?

View controllers act as a data source or delegate for another object. View controllers are often used to manage the data for tables, and collection views. You can also use them as a delegate for an object such as a CLLocationManager object, which sends updated location values to its delegate.


1 Answers

I think the exact same implementation may not be possible in Swift. From the Swift book section about overriding properties:

“You must always state both the name and the type of the property you are overriding, to enable the compiler to check that your override matches a superclass property with the same name and type.”

However, you could use a computed property that returns a typecast version of your view controller's view property, and it would be almost as clean:

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
    }
}
like image 187
Nate Cook Avatar answered Oct 05 '22 04:10

Nate Cook