I often heard, that it is not good to organize classes the way every class knows every other class.
So I try to let the classes that are as a property in other classes not know about their parents. But with UIView I can't make it happen.
The question comes from a more generic background, because the problem occurs not only on UIViews.
If I have a car as object with 4 wheels as 4 instance variables and when the left front wheel bursts. The left rear wheel should get a message. How should I design the system in a good way?
An example with UIView in Objective-C:
I have an custom UIView filling the whole screen. managing all the layout. Let's say only 2 subviews keep it simple.
#interface BackgroundView : UIControllerView {
CustomViewA *buttonA;
CustomViewB *buttonB;
}
#end
#interface CustomViewA : UIButton
#interface CustomViewB : UIButton
Now if someone presses button A, button B should do something (e.g. turn red).
There are several options I see:
Set the delegate of button A to button B, so all event of A goes to implementation file of B. Problem if not all events have to do with button B.
Create a singleton of BackgroundView and let button A get the property of button B or call a method in BackgroundView that does forward my call to button B. Problem if I want more than one Background, could create a even higher class to have the two BackgroundViews (doesn't sound nice).
Call parent implemented for UIViews and then like 2. call a method or change it directly. Problem when not using UIView, copy the functionality to other 'parent' classes ?
Is there a better way of solving such a problem? Or can I optimize one method greatly?
#Odrakir mentions Model-View-Controller here, and they're right -- that should be your first stop. In case it helps, here's how you might apply that pattern to your example:
You have a model class with a property color. In a document-based application, the document typically is the model. In a non-document-based application, it's common to hang the model off of the application delegate instance. For simplicity's sake, let's say we're in the latter case. You might have the following classes:
// "Model"
#interface MyModel : NSObject
#property (nonatomic, readwrite, copy) UIColor* color;
#end
// AppDelegate
#interface MyAppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic, readonly, retain) MyModel* model;
#end
// "View"
#interface CustomViewA : UIButton
#end
#interface CustomViewB : UIButton
#end
// "Controller"
#interface MyViewController : UIViewController
#property (nonatomic, readwrite, assign) IBOutlet UIView* buttonA;
#property (nonatomic, readwrite, assign) IBOutlet UIView* buttonB;
- (IBAction)doActionA: (id)sender;
- (IBAction)doActionB: (id)sender;
#end
The basic pattern is this: Actions from the view trigger model-modifying actions by the controller (the target/action pattern). To achieve this, you would hook them up by connecting the action of button A to -doActionA: on the controller ("File's Owner" in IB) and (likewise for B). Then the action methods on the controller, when triggered, should modify the model, and invalidate any views that would need to be redrawn as a result of the model change. You might do this by plugging the views into IBOutlets on the controller and having the controller call [self.buttonA setNeedsDisplay]. Then, when your view draws, it should read, from the model, any state that it needs to draw correctly.
It's worth mentioning that on MacOS, you can eliminate the manual invalidation step by using bindings to link the view to the model. In that case, changes to the model will automatically invalidate the view. iOS/UIKit doesn't have bindings, so you have to do this invalidation by hand.
Probably all that logic belongs in a ViewController who knows everything about the views he controlls but the views don't know anything about the view controller or about each other.
Views should communicate with the view controller through delegation (or target-action) and is the view controller the one that should make decisions and maybe, forward messages to other views.
By the way, take a look at Stanford courses in iTunes University. I think it's the first or second class where they talk about MVC (Model View Controller). It's going to help a lot: https://itunes.apple.com/us/course/coding-together-developing/id593208016
Related
I occasionally override the setters of Objective-C properties, and was wondering. If I change the default behavior of a method drastically, where should I document this in the header? Or should I just use a new method completely?
In my current case, I am setting a view up as a placeholder view in interface builder. Programatically, there will be a way to replace this view with a new view (Either an icon, or an arbitrary custom view). The method to swap out the placeholder view will automatically set the property to the new view, remove the placeholder view from the parent view, add the new view to the parent view, and reposition/resize the new view appropriately.
I came up with three options:
A) Override the setter of the property, and document along with the property:
// Documentation goes here
// The setter of this property actually does <etc>
#property (nonatomic, retain) IBOutlet UIView* placeholderView;
B) Override the setter, and declare it in the header:
// Documentation goes here
-(void)setPlaceholderView:(UIView*)view;
C) Use a completely different method and set the property to readonly:
#property (nonatomic, retain, readonly) IBOutlet UIView* placeholderView;
-(void)replacePlaceholderView:(UIView*)view;
Option C seems appealing, because it makes it quite clear what the method does. It will also be clear that since it is different than a normal setter, it may act differently (which it will). The disadvantage I see here is that it doesn't seem to follow the normal Objective-C trend.
What do you guys think is the cleanest way to do something like this?
You shouldn't "change the default behavior of a [setter] method drastically" at all. A setter method should set a property, and if it does, you don't need to document the override in the header anywhere, since it does exactly what the user would expect, and the override is an implementation detail that can be documented in the implementation file. Radically changing expected behavior is only going to sow confusion, and is something to avoid. Go with option (C) unless your desired behavior is what a user would reasonably expect to happen when she sets that property.
In practice, an IBOutlet should only be set during initialization from nib files. If I was to refactor this, I'll declare the properties
#property (nonatomic, weak) IBOutlet UIView *initialPlaceholderView;
#property (nonatomic, weak) UIView *placeholderContainerView;
#property (nonatomic, weak) UIView *currentPlaceholderView;
then rename replacePlaceholderView: to
-(void)updatePlaceholderContainerWithNewView:(UIView*)newPlaceholderView;
This is self-documenting code. You can already assume the behavior just by the property and method names.
Consider an example when I'm having UIViewController with simple interface:
#interface MainViewController : UIViewController
#end
When it receives viewDidLoad or viewDidAppear messages it creates and places additional views.
When unit testing it, I want to mock up these views, so I need to change interface to this:
#interface MainViewController : UIViewController
#property (nonatomic) UIView *additionalView1
#property (nonatomic) UIView *additionalView2
#end
By doing this I'm moving responsibility of creating additional views to calling side, which, in my opinion, breaks encapsulation.
How can I avoid exposing so much of controller internals?
To support setter injection, you really have to expose it.
There are ways of trying to hide it, or mark it as off-limits. You can wrap the property declarations in #if DEBUG. Or you can move them to a class extension in MainViewController_Private.
But I find that these tricks only make the code noisier. So my approach is to go ahead and expose them. As I point out on Testability, Information Hiding, and the Class Trying to Get Out, such exposure can be a clue that a class may need to be extracted, or responsibilities shifted. For example, since you want to inject these views, should MainViewController really create them? Maybe the tension is leading us to make a factory, keeping MainViewController ignorant of the details of these views.
This is a question rather many people have asked, especially here on StackOverflow.
Reloading the data on the table view is easy, [self.myTableView reloadData];, myTableView is the instance of my UITableView, since I am using a UIViewController instead of a UITableViewController.
I want to reload the table view from another view controller after I have updated the data (from Internet). The data is contained in a property list. I have tried using protocols, notifications and some other things like putting it in viewDidAppear:. Nothing have worked for me.
Is it something I haven't thought about or have I just done some of the methods wrong? The help is much appreciated!
If you want access to a UITableView object, or just about anything else for that matter, from another class, you can make it accessible using a property:
ClassA.h
#interface ClassA {
UITableView *tableView;
}
#property (nonatomic, readonly) UITableView *tableView;
#end
ClassB.m
- (void)reloadTableInOtherClass {
[classAVariable.tableView reloadData];
}
Using observer pattern + notification is a good way. And to let your view controller decide when to reload data is also a good practice.
Why notification does not work. Did you use addOvserver: ?
I've been looking at the API for IOS Programming, and have been reading about view controllers and UIViews. It looks like UIViewController subclasses are really useful for Modal navigation and custom animation, but I can't see any other uses than that.
What is the benefit to using a UIViewController subclasses rather than a normal NSObject subclass?
Why
#interface MyViewController : UIViewController {
}
-(void)handleEvent;
#end
Instead of just
#interface MyViewController : NSObject {
UIView* view;
}
#property(retain) UIView* view;
-(void)handleEvent;
#end
Don't you just end up adding just the view to the window, not the actual viewController itself?
For most purposes, isnt all of the functionality you need encapsulated within the UIView object?
You just end up adding it like this:
[window addSubview:myViewControllerInstance.view]
Is there a use for UIViewController other than built in functionality like Modal Navigation?
Thanks.
(Sorry if this is a stupid question, I've been learning this for 2 days now)
Cocoa on Mac OS and iOS make heavy use of the Model-View-Controller (MVC) pattern. Following the MVC model, UIViewController is a Controller class. Its job is to coordinate interactions between your user interface (View objects) and your application data (Model objects). More basically, the Controller is primarily where you place your application's logic. It handles events and calls upon the view and model accordingly.
See the UIViewController reference, which has a nice overview on the class.
By deriving from UIViewController, you get a bunch of Controller functionality for free, such as loading views from a Nib file (initWithNibName:bundle:) and responding to view-related events (viewWillAppear:, viewWillDisappear:). Additionally, UIViewController is itself a subclass of UIResponder, which contains functionality for handling touch events (touchesBegan:withEvent, touchesMoved:withEvent, touchesEnded:withEvent).
Basically, there's no reason NOT to use UIViewController with all the functionality it provides. Even if you could manage to to do so, it would be way more work, for no real benefit.
Take a look at the properties and instance methods of the UIViewController Class, which you would not get for free if you just subclassed NSObject. There is a lot of stuff in there that you will use in all of your applications.
I have a custom UITableViewController subclass which I use in two places in a nib file. What should I do if I want the two instances to have slightly different behavior? Of course in the code I can select one kind of behavior or the other based on the value of a BOOL, but how do I set that BOOL from Interface Builder, without having to write an Interface Builder plugin?
As of Xcode 6 there is a new way doing this. You can now give your view properties the attribute IBInspectable and then you can edit those properties in IB as you would with and standard view.
So for example:
#property (nonatomic, strong) IBInspectable BOOL
More details (also for the new attribute IBDesignable) in Apples documentation: https://developer.apple.com/library/ios/recipes/xcode_help-IB_objects_media/chapters/CreatingaLiveViewofaCustomObject.html
"User Defined Runtime Attributes" in the Identity inspector is probably what you're looking for. This seems to be new as of Xcode 4.2.
Unfortunately, there doesn't seem to be much (any?) documentation about this feature on the Apple Developer site. I was able to use it for a simple property set.
So far as I know, you can't set parameters in IB without writing an IB Plugin.
That said, you have two other options.
If it is as simple as a single BOOL, you're probably best off making it a property of the MyCustomViewController class and set it in code after you init:
customViewController = [[MyCustomViewController alloc]initWithNibName:#"CustomViewController" bundle:nil];
[customViewController setFunky:YES];
The other option is to create a protocol for a MyCustomViewDelegate. If you're not familiar with protocols, your header would look like this:
#class MyCustomViewController;
#protocol MyCustomViewDelegate
#required
-(BOOL)customViewShouldBeFunky:(MyCustomViewController*)customView;
#end
#interface MyCustomViewController : UIViewController {
NSObject<MyCustomViewDelegate> *delegate;
}
#property (readwrite, retain) IBOutlet NSObject<MyCustomViewDelegate> *delegate;
#end
Since it is an IBOutlet, you can wire up the delegate like any other delegate in Interface Builder.
Then call [delegate customViewShouldBeFunky:self] when you need to determine how your view should behave.
Have two subclasses is probably easier, and will be easier to document.
Here is an example of overriding properties and setting them in custom classes, this may help. The property code will work before awakeFromNib is called. So you may decide what you have to do based on the user's decision right in awakeFromNib.
https://stackoverflow.com/a/31094561/1699210