I've got a UI with several elements, some of which I'd like to encapsulate into their own objects. For example, one object would be a timer (basically just a UILabel sitting in a UIView) with the externally-available members:
startTime (property)
start and pause (methods)
(And also a im_done NSNotification when the timer reaches 0.) This object and several others would be used together in a single UIViewController.
Would a UIView or UIViewController be more appropriate to subclass for the timer object (and all the others)? Reading this answer leads me to believe UIView, but for my specific case (especially for the more complicated objects), I'm not sure. Thanks for reading.
EDIT: Just to clarify, I would want all code that implements timer functionality separate from my main view controller. One big reason is so that the timer object would be portable.
You should have a subclass of UIView, but it should just have the view components to display whatever time information you need. You should also have a MyTimer class which encapsulates the timing functionality and provides callbacks on changes (such as the time remaining and perhaps the completion as a separate when the time remaining reaches zero). Ideally the callbacks pass the MyTimer and, as a convenience, the remaining time value so you don't need to use the accessor method in the simple use case).
Now, you already have a view controller which is managing your broader view, and it can create add your UIView subclass to the main view and create, own and manage the associated MyTimer instances which drive those views.
This I think comes down to preference. You'll include a uiview and uiviewcontroller in a parent view controller differently and this difference can make a uiviewcontroller more difficult if you don't understand containers. I would say best practice would be in a uiviewcontroller but it really is up to you.
Related
I'm facing with a complex design problem. Due to a hard designed graphic I can't use Apple navigation pattern as UINavigationController or other ones.
This is the app diagram
Black arrow: protocols direction
Blue arrow: parent-child pattern direction
I created this flow because I want to maintain the code inside the single ViewController clear and isolated: I thought about them as modules that they can be used somewhere in other apps.
The RootViewController is the MainVCs position manager. It decides which is the current view-controller that it must be showed and it configures it based on its status (modified by delegate methods).
The single MainVC doesn't know that RootVC exists and it call Root using protocols. The single ChildViewController doesn't know that its MainVC exists and it call the Main using protocols and so on.
Code is clear and much easy to understand, my purpose, but when I have to apply a modify to the skeleton (RootVC) from the ChildViewControllerN, child of the umpteenth ChildViewController, child of the umpteenth MainViewController, I have to propagate the protocol until the RootViewController.
My first question is: is this pattern correct? What do you think about it?
My second question is: is there a limit point where I haven't to use delegate pattern but something like KVO?
ADDING
I read a university lecture about the composite pattern that it says:
A composite object knows its contained components, that is, its children. Should components maintain a reference to their parent component?
Depends on application, but having these references supports the Chain of Responsibility pattern
So, according to the Chain, I can maintain a reference of the parent but I have to declare a custom interface for this kind of reference. However, doing this I will decrease the number of protocols, but the second question still be unanswered
Opinion:
When I go beyond a single level of parent/child relationships, I tend to stop using delegates and move to NSNotification. Frequently, I go directly to NSNotification to reduce dependencies. I prefer that to KVO because it is explicit, whereas KVO is harder to debug as the project progresses.
(Example: what looks like a simple variable assignment on a background thread results in a hard-to-diagnose crash if a listener is deallocated on the main thread between the moment of assignment and the KVO delivery.)
I have created a custom class for my UIBarButtonItem (refreshIndicator.m). This button will be on many different view controllers, all push-segued from my MainViewController/NavigationController.
Instead of dragging an outlet onto every single ViewController.m file for iPhone storyboard THEN iPad storyboard (ugh, still targeting iOS7), I want to know if there is a way to complete my task simply within my UIBarButtonItem custom class. I've looked around everywhere but I haven't quite found an answer to this,
All I need to do is check which UIViewController is present, check the last time the page was refreshed, and then based on that time, set an image for the UIBarButtonItem. (I've got this part figured out though, unless someone has a better suggestion). How can I check for the current UIViewController within a custom button class? Is this possible?
Does it need to know which view controller its on so it can tell that vc it was pressed? If that's the case, then use your button's inherited target and action properties. On every vc that contains an instance of the button, in view did load:
self.myRefreshIndicator.target = self;
self.myRefreshIndicator.action = #selector(myRefreshIndicatorTapped:);
- (void)myRefreshIndicatorTapped:(id)sender {
// do whatever
}
More generally, its better to have knowledge about the model flow to the views from the vc, and knowledge of user actions flow from the views. Under that principal, your custom button could have a method like:
- (void)timeIntervalSinceLastRefresh:(NSTimeInterval)seconds {
// change how I look based on how many seconds are passed
}
And your vcs:
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:self.lastRefreshDate];
[self.myRefreshIndicator timeIntervalSinceLastRefresh:interval];
If you really must go from a subview to a view controller, you could follow the responder chain as suggested in a few of the answers here (but I would go to great lengths to avoid this sort of thing).
It is possible to achieve this, but the solution is everything but elegant. It is one way of getting around the basic principles of iOS and is strongly discouraged.
One of the ways is to walk through the responder chain, posted by Phil M.
Another way is to look through all subviews of view controllers until you find the button.
Both ways are considered a bad practice and should be avoided.
For your particular case, I would rethink the structure of having a separate instance of the bar button. For example, you could rework it into a single UIButton instance that gets displayed over every view controller and it can also act as a singleton.
Actually this is a simple question with maybe not an answer for a central solution.
I would like a way to monitor in a central way every UIView start-finish loading to get metrics of the application.
I can see that viewWillLoad doesn't exist anyway in the UIViewController class and viewWillAppear is not something that it could serve the purpose.
Is this feasible in any way? I'm thinking of searching every UIView inherited class in the application and inject code somehow, but as I said I will need two methods.
Or maybe inject code to a protocol that already exists in the UIView class?
Any thoughts?
Regards.
If you are only concerned with the loading of the root view of every view controller. You could swizzle -loadView and -viewDidLoad of UIViewController. You could exchange those with methods that add start and end times to a table for each instance that calls them. Keep in mind though, that just the act of measuring it, you are going to slow down the loading.
If you want to track the loading of every view, you could swizzle -initWithFrame on UIView in the same way. In your version of the method you would start tracking before you call the real version and stop tracking once the real method has exited. However, if you have custom views that have their own initializers you will have to come up with a more complex version of that. However I do not recommend doing this. The time it takes to initialize a view is probably less time or at least very close to the amount of time it will take to track it making the timings worthless.
I'm trying to refactor my code in the best possible way and I'm wondering what the proper architecture for the given situation is.
What I'm Trying To Do
What I'm doing is pretty simple: I have some custom CALayer subclasses that represent an interactive UI element. They are broken up into multiple layers since some of the parts of the UI are static, so I didn't want to redraw those static elements needlessly. Right now, the layers are added as sublayers in the initialization part of a CustomView class that is a subclass of UIView.
There is currently no corresponding CustomViewController class that is a subclass of UIViewController because when I'm using the CustomView, it's contained within a UITableViewCell or a part of a generic UIViewController with other views in it, so I felt another UIViewController for each CustomView instance would be redundant.
Also of importance is the that the only operation that I'm doing inside of the UIView class is I'm responding to touch events and sending the touch information to the sublayers so that the UI can update its appearance accordingly. I'm not overriding the drawRect method or anything like that.
The Question
Basically, I'm trying to figure out whether I should either:
Option 1:
Get rid of the CustomView class, create a CustomViewController class that is a subclass of UIViewController, and simply add the CALayer objects as sublayers of the CustomViewController's built-in view property.
or
Option 2:
My thinking about the UIViewController subclass being redundant is correct, so I should leave it the way I have it and have a CustomView class with the CALayer objects inside of it.
I would highly appreciate any advice on this.
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.
I would like to know how to best possible address the following issue:
I have a single ViewController. Its view contains a great number of complex subviews (subclass of UIView). Due to the complexity some of these UIViews initialise their own UIGestureRecognisers and implement the according target actions. As I want to coordinate the gestures of various subviews I have to set the single once ViewController as the gesture's delegate.
There are various possibilities for that.
1) Initialize ALL gestures in the the viewController (this will lead to a massive viewController)
2) defining a protocol in the UIVIews (getViewController), implemented by the ViewController
#protocol CustomViewDelegate <NSObject>
#required
- (UIViewController *)getViewController;
#end
3) customise the init method of the UIViews and using the ViewController as an option.
- (id)initWithFrame:(CGRect)frame andViewController:(UIViewController *)vc;
What is the most elegant possibility to solve this issue? Is it OK to implement target actions inside a UIView object?
Thanks for your thoughts...
If you're defining custom UIView subclasses, you can invest them with as much logic as it makes sense to store local to them, give them delegate protocols to pass anything else up and, as long as you expose the delegate as an IBOutlet, you can wire up your view controller as the relevant delegate directly in Interface Builder or the UI designer part of Xcode 4. I personally think that would be the most natural way forward, since it consolidates any view-specific logic directly in the view and lets you do the wiring up where you would normally do the wiring up.
In terms of overall design, such a scheme conforms to model-view-controller provided your views are doing only view-related logic. So, for example, if you had a custom rectangular view that can take a swipe anywhere on it to reposition a pin, and the 2d position of the pin affects some other system setting, you'd be correct to catch the gesture in the view, reposition the pin and then send updates on its position down to the delegate, which would fulfil the role of controller and push the value to any other views that are affected and out to the model.
Commenting on your suggested solutions directly:
(1) this would focus all logic into the one controller; whether it's correct from a design point-of-view depends on the extent to which you're having to interrogate your custom views (in that you don't want to end up treating them as mostly data that external actors have to know how to manipulate) and the extent to which you want to reuse logic.
(2) I'm not sure I entirely understand the suggestion — what is getViewController defined on and how does it know how to respond? If it's the UIViews themselves and the view controller has to identify itself first then I'd suggest just adopting the delegate pattern wholesale rather than specialising to views and view controllers, e.g. as you may want to build compound views that tie together logic from several subviews.
(3) as a rule of thumb, the sort of things to pass to init are those that the class actually needs to know to be able to initialise; it would probably be better to use a normal property to set the controller after the fact. Or make it an IBOutlet and wire it up so that it happens automatically via the NIB.