Why Should Not Directly Extend Uiview or Uiviewcontroller

Why should not directly extend UIView or UIViewController?

This approach is preferable to using UIView directly, as in

extension UIView {
func flash() {
...
}
}

because it lets programmers decide which UIView subclasses they wish to make Flashable, as opposed to adding flash functionality "wholesale" to all UIViews:

// This class has flashing functionality
class MyViewWithFlashing : UIView, Flashable {
...
}
// This class does not have flashing functionality
class MyView : UIView {
...
}

Essentially, this is an "opt in" approach, while the alternative approach forces the functionality without a way to "opt out".

When to use a UIView vs. a UIViewController on the iPhone?

From Apple's View Controller Programming Guide for iOS:

"The most important role of a view controller is to manage a hierarchy of views. Every view controller has a single root view that encloses all of the view controller’s content. To that root view, you add the views you need to display your content."

Also:

"There are two types of view controllers:

  • Content view controllers manage a discrete piece of your app’s content and are the main type of view controller that you create.
  • Container view controllers collect information from other view controllers (known as child view controllers) and present it in a way that facilitates navigation or presents the content of those view controllers differently.

Most apps are a mixture of both types of view controllers."

Assign new UIViewController or a UIView directly?

Do like this.

In viewController class on which event you want to switch the view at there use this,

-Make object of appDelegate class,
-then on this object access the window .
-Make object for new view
-and add it on window.

see this,

    YourAppDelegate *obj=(YourAppDelegate *)[[UIApplication sharedApplication] delegate]; 

abcViewController *objAbc=[[[abcViewController alloc] initWithNibName:@"abcViewController" bundle:nil] autorelease];

[obj.window addSubview:objAbc.view];

UIView or UIViewController subclass?

I think that in terms of MVC, the code you're describing (option #2) is well written and maintains a very clear boundary of responsibility. You're not writing any code that has nothing to do with the view layer itself in this class which is great. I think that in this case there's no need for a separate UIViewController subclass to manage these instances because as you said - they are handling their own touch events and visible layers (exactly their responsibility).

If for any reason there is a need for something more complex that requires data related logic or other such computation, I would definitely consider subclassing a UIViewController or maybe looking at the problem in an entirely different way.

Given the situation you've presented, I think that maintaining the CALayer instance within this UIView subclass ('CustomView') is the right way to go.

Am I abusing UIViewController Subclassing?

Answer to title question: Yes.

So basically, either I have been
completely wrong, or I'm on a wild
goose chase.

It sounds like you've been completely wrong. The term "view" has a few different but related meanings:

  • A view is, of course, any object that's an instance of UIView or a subclass of UIView.
  • In the context of MVC, "view" is used collectively, and we talk about this or that being "the view's responsibility" even though "the view" is really a group of objects.
  • When talking about a view controller, the "view" that the controller manages is the UIView instance that the controller's view points to and the hierarchy of subviews that it contains.

It sounds like your misunderstanding is on this last point. A view controller should manage a single "screenful" of content. If you're using a single view controller object to manage more than one view hierarchy, or if you're using several view controllers to manage different parts of the same view hierarchy, you're using UIViewController in a way which was never intended and which is likely to lead to problems.

The methods that you mentioned (-viewDidLoad, -viewWillAppear, etc.) are meant to tell the view controller that its view hierarchy was just loaded, is about to be displayed, and so on. They're really not meant to refer to an individual subview, and it would be unusual for a view controller to need to be given that information for individual subviews. If the view hierarchy was loaded, then the view controller knows that everything in that hierarchy was loaded.

You seem to be interpreting these methods as delegate methods, but they're not. A delegate is a separate object that allows for customization of the delegator without the need for subclassing. -viewDidLoad and -viewWillAppear are two examples of override points for UIViewController, a class that's intended for subclassing. The view controller object calls these methods itself to give subclasses a chance to take some action at an interesting point in the controller's life cycle.

If UIViewController subclasses can't
be counted on to call viewWillAppear,
why not just call that method
manually, and be done with it?

Take a good look at UIViewController and you'll see that most of the functionality provided has to do with displaying the view (that is, the view hierarchy) on the screen, or with integrating the controller with "container" view controllers such as UINavigationController and UITabBarController. None of that is useful to objects that aren't managing the entire screenful of content.

It happens sometimes that a group of views will replicated on several screens, and in some of those cases it's helpful to manage those views with an object that's separate from the view controller itself. I can see how you'd be tempted to use UIViewController because of its -viewDidLoad and similar methods, but those are really only a small part of what UIViewController does. What would it mean to call -presentModalViewController: on one of those objects? Or to access its navigationController or parentViewController properties?

If you really want to manage subviews of your view controller's view hierarchy using those methods, create a subclass of NSObject that has -viewDid[Load|Unload|Appear|Disappear] and -viewWill[Appear|Disappear] methods. You can create that class once and then subclass it as often as you need to, and none of your "subcontroller" classes will have all the extra, unneeded controller management stuff that comes along with UIViewController.

Edit: I want to add a pointer here to Apple's View Controller Programming Guide for iOS, which provides a lot of support for what I've laid out above. Here's a relevant passage from the subsection titled "View Controllers Manage a View Hierarchy":

View controllers are directly
associated with a single view object
but that object is often just the root
view of a much larger view hierarchy
that is also managed by the view
controller. The view controller acts
as the central coordinating agent for
the view hierarchy, handling exchanges
between its views and any relevant
controller or data objects. A single
view controller typically manages the
views associated with a single
screen’s worth of content, although in
iPad applications this may not always
be the case.

View Controller Programming Guide is required reading for anyone even thinking of writing an iOS app. It's worth reviewing if you haven't read it in a while (or ever).

Update: Starting with iOS 5, it's now possible to define your own container view controllers, i.e. view controllers that manage other view controllers and potentially display the views of multiple view controllers at the same time. You can read more about it in the guide linked above in the section entitled Creating Custom Container View Controllers. None of this really changes the essential points above: a single view controller should still manage a hierarchy of views, and methods like -viewDidLoad still refer to the entire view graph rather than to individual subviews. The advice that a view controller manages an "entire screenful" of content is no longer completely accurate -- just as UISplitViewController has displayed content from two view controllers simultaneously ever since the introduction of the iPad, your own containers can now show the views of multiple child view controllers. Writing a container view controller is a somewhat advanced topic -- you should be very familiar with the use of view controllers generally and the way the provided container view controllers work before you take a stab at creating your own.

Why extend class instead of conforming to a protocol?

Technically (i.e., in effect), there is no difference. The two methods will behave the same. However, this allows the programmer to put all the code required for the protocol in one place.

tl;dr: it's a stylistic choice.

Why can't I change UIView Class of root View of a ViewController

It's not a matter of not being able to subclass UIView (as you have seen already), it's a matter of the compiler recognizing your subclass. self.view is declared as a pointer to a UIView object, not of your subclass, so you have to do a little extra work to access it's properties; namely: casting. To access "myProperty", use [(NewView*)self.view myProperty];. Simple as that.

You have to cast as a sort of promise to the compiler that literally states "self.view has the class NewView, not what you think it is, so recognize it as such."

As a side note, I would also make sure that the class of the root view in the XIB is set appropriately (meaning, it needs to be of class NewView, else when you try to message it, your cast will fail and return a UIView object, which does not respond to -myProperty, thus throwing a generic exception).

accessing UIView subclass in view controller

Posting my own answer.

  1. Create the XIB file.
  2. Create the UIView subclass Swift file.
  3. Under the XIB file owner's Identify Inspector custom class field, type in the UIView subclass name (your custom view).
  4. Under the XIB file owner's Connections Inspector, make sure all IBOutlets in the Swift file are connected.
  5. Add a view to the view controller and under its Identify Inspector custom class type, specify the custom class name.

Important:
* In your XIB swift file, you have to properly load the XIB content view.

...

/// Initializer used by Interface Builder.
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}

/// Initializer used programmatically.
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}

...

func configure() {
let contentView = // here use many of the functions available on the internet to
// load a view from a nib.
// Then add this view to the view hierarchy.
addSubview(contentView)
}

Can I extend viewWillAppear in UIViewController?

Ok, after finding this http://nshipster.com/swift-objc-runtime/ I tried something similar and could achieve the desired result.

The code looks like:

extension UIViewController {
public override class func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}

// make sure this isn't a subclass
if self !== UIViewController.self {
return
}

dispatch_once(&Static.token) {
let originalSelector = #selector(UIViewController.viewWillAppear(_:))
let swizzledSelector = #selector(UIViewController.nsh_viewWillAppear(_:))

let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}

// MARK: - Method Swizzling

func nsh_viewWillAppear(animated: Bool) {
self.nsh_viewWillAppear(animated)
NSLog("viewWillAppear: \(self)")
}
}


Related Topics



Leave a reply



Submit