Does styling UI objects in iOS violate MVC? - ios

I'm somewhat new to MVC and iOS development, and I can't seem reconcile how UI styling fits into this paradigm.
My view of MVC is built using storyboards, and I can apply primitive styling through Xcode's attribute inspector, but anything more complicated I have to use the Controller to style. For example:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated]; // required
// set background color of view
[[self view] setBackgroundColor:[UIColor darkGrayColor]];
}
This seems to be a clear violation of MVC, as I'm applying style logic inside of the controller's code. I find this analogous to writing an HTML app and instead of using style sheets, I write code to apply styles locally in JavaScript. Is this a weakness of iOS or am I just doing it wrong?

Taken from Apple's docs :
some controller objects might also tell a view object to change an
aspect of its appearance or behavior
And it does make sense as the view is supposed to be passive and only reflect the application state as a UI and the controller will "tell" the view if some of its content needs to be changed according to user actions. (e.g background change, visibility of controls etc...)

Actually the screen is your "view", and your "controller" is sending a message to your view to use a different color for the background.
If you had a data object that held the screen color, that could be your "model". In that case, you'd be passing the data from your model to the view through the controller.

You can mix and match the two within your code. If you want you can even manipulate Model details in the Controller as well. The MVC is not strictly enforced in the general guidelines of the pattern, but if you want to stay true to the paradigm you just have to refrain from using M or V in C.
View entails cosmetics and aesthetics of the forms as well as styling, but bottom line, you can manipulate these facets from the controller...

As others said, if you understand colors and styles as data, it is ok to let the controller take care for it.
But you also could subclass UIviews and internally set the style.
Let's say, you have a TrafficLightView : UIView with a property id trafficLight. you could overwrite the setter and set the background color of the view accordingly to the state of the object. trafficLight.

Related

Style elements defined in Interface Builder are not being applied when instantiating from Storyboard

I have an iOS app developed in Xamarin.iOS (C#, Monotouch) where the primary UI is NOT storyboard-based. (I do this because my app needs structurally different layouts in portrait and landscape orientations, and it is a great deal easier to achieve that programmatically than through IB and Storyboarding.)
My problem is that I'm now trying to use a Storyboard to develop a simple dialog, but when I instantiate the dialog the structure is there but the style elements defined in Interface Builder are not being applied. Everything I've read seems to suggest that this should just happen. This is particularly problematic as most of the style elements cannot be modified after the interface is initialized.
Here's the code where I do the instantiation:
UIStoryboard sb = UIStoryboard.FromName("StoryboardAppSettings", null);
var vc = sb.InstantiateViewController("TableViewControllerAppSettings");
UIViewController settingsVC = vc as UIViewController;
PresentViewController(settingsVC, true, null);
I have defined a UITableView that is to be "grouped", but it isn't. I have two UISwitch elements with colors defined that are not being applied. I have buttons whose tint colors are not being applied.
(If I set a breakpoint and drill down into the view controller data structures, I find that all these parameters ARE set correctly...they just seem to be ignored when the hierarchy is realized.)
What am I doing wrong?
I found that the problem was that I was setting UIView.Appearance.BackgroundColor. It seems that when you do that, the UIView.Appearance.BackgroundColor ALWAYS overrides UISwitch.OnTintColor. (In my mind that is a bug, probably in iOS, but I may misunderstand how the Appearance API is supposed to work. My expectation is that a generalized global setting (like UIView.Appearance) can be overridden at more specific levels (descendent views or specific instantiations), and I believe that is the way it works for most other Appearance properties. UISwitch.OnTintColor (as well as UISwitch.TintColor and UISwitch.BackgroundColor, but NOT UISwitch.ThumbTintColor) seems to be the exception (and hence bug), not the rule.

MVC Model - Should controller access view's controls directly?

I'm learning iOS development stuff and what I have found in tutorials and books is that controller layer usually has access to the View's controls directly (textfields, labels etc.). Let's consider such an example:
Assume, that View has a label called lblResult and a textfield called txtDataToAnalyze. Than in controler interface we've got something like this:
#property (nonatomic, retain) IBOutlet UILabel* lblResult;
#property (nonatomic, retain) IBOutlet UITextField* txtDataToAnalyze;
and some #synthesize statements in the implementation file.
I have some experience with JavaSwing development, where most of thinks I'm writing manually without any GUI Builders, and what I usually do in MVC is to access the View's controls via getters/setter. For example: void setResult(String resString); or String getDataToAnalyze();. In that way, controller knows only what pieces of information are displayed in the view, and not how are they displayed. I think it is more flexible (it is easier to change the view layer later).
I know that iOS has some specific rules, has introduced XIB/NIB files etc so maybe my doubts are completely useless in case of iPhone/iPad development. But I am going to write some more serious application for iOS (actually "rewrite" it from Java Swing) and that's why I would like to ask you:
Do you think, I should change the way I am thinking and get accustomed to that new (for me) approach (xib files, creating GUI using drag&drop and providing controler with information about how data should be displayed in view) ?? Did you have similar doubts when starting with iOS?
Short answer:
Yes, I think you should definitely spend a little time getting accustomed to working with Interface Builder (IB) to make NIBs and storyboards and let IB create the IBOutlet and IBAction references for you for those controls with which you need to interact. Once you're proficient at it, you'll be impressed by your productivity in generating easily maintained code. Don't dismiss IB too quickly.
In terms of letting the controller interact directly with the IBOutlet and IBAction references, this is common practice for simple user interfaces. If you have some real world examples, post a new question with a screen snapshot and we can offer more practical guidance.
Long answer:
Part of your question seems to be driven by the apprehension in seeing view controllers that are doing detailed interaction with a view's controls. The thing is, if you want to isolate your controller from some of the implementation details of the view, then go ahead and subclass the view and put the view specific stuff in there. IB can interface with both view controller subclasses as well as view subclasses. So you can happily use IB and still isolate your view controller from some of these implementation details.
Personally, I only do this subclassing of UIView when the view hits some subjective complexity threshold (e.g. for me, that threshold is when I find myself doing some complicated animation, such as using CADisplayLink; complicated gesture recognizers, etc.). I also subclass those subviews that are logical entities of their own (e.g. UITableViewCell or UICollectionViewCell). But for simple views where I'm interacting with my model to setting a control's properties, interacting with text fields, etc., I think putting that in the view controller is fine. Having said that, if I have a lot of view-specific code in my controller which has nothing to do with the integration of my model with my view, then start subclassing the UIView and shifting the view-only code into that.
Implicit in your question is the notion of programmatically building view rather than using NIBs/storyboards. In my opinion, using Interface Builder (IB) to build your UI is much easier to develop and maintain. There might be some pedagogical value to doing a test project where you build your views programmatically, so you really understand what's going on, but after that, I think you'll find yourself quickly gravitating to storyboards. And you'll get plenty of chances to write your own non-IB code when you start doing things beyond the capabilities of the standard IB controls (e.g. complicated custom container views, etc.). There are definitely those who prefer to develop views programmatically, but I don't think you can beat the development speed and ease of maintenance of IB generated UIs.
I general, the controller does not know about the view, but the view knows about the controller.
The gang of four book says:
"MVC also lets you change the way a view responds to user input without changing its visual presentation. You might want to change the way it responds to the keyboard, for example, or have it use a pop-up menu instead of command keys. MVC encapsulates the response mechanism in a Controller object. There is a class hierarchy of controllers, making it easy to create a new controller as a variation on an existing one.
A view uses an instance of a Controller subclass to implement a particular response strategy; to implement a different strategy, simply replace the instance with a different kind of controller. It's even possible to change a view's controller at run-time to let the view change the way it responds to user input. For example, a view can be disabled so that it doesn't accept input simply by giving it a controller that ignores input events.
The View-Controller relationship is an example of the Strategy (315) design pattern. A Strategy is an object that represents an algorithm. It's useful when you want to replace the algorithm either statically or dynamically, when you have a lot of variants of the algorithm, or when the algorithm has complex data structures that you want to encapsulate."

Where do I create custom UI elements?

A quick question from a wanna-be iOS developer. I want to create a UI for an iPhone app without Interface Builder, only programmatically. However, I want to stick to MVC recommendations and separate V and C and have a clean readable code, therefore:
I create UIView class files (e.x. SplashView.h and SplashView.m)
I create UIViewController class files (SplashViewController.h and SplashViewController.m)
I define my UI elements (view, subviews, buttons and text fields) in the UIView class files
I load the main view in view controller's loadView method, and then do other things in view controller's viewDidLoad method
Is this a correct approach to begin with?
Second part of the question, independent of Y/N answer to the first. Where do I define these custom UI elements?
- Inside the view's initWithFrame: method?
- In separate (property getter? property setter?) methods? I.e. do I have to declare each UI element as a property first in the .h file?
If these questions sound a bit ignorant, it must be because they are :) I found lots of sample code on StackOverflow, but little to indicate where you actually put it. I would be really grateful for any help, especially if you could paste/reference some relevant code.
Your list is correct. This is how I do all of my apps. No Interface Builder, just code.
Each custom view typically creates its own subviews in an appropriate initXXX method. This could be initWithFrame: but you could define others as needed. Subview layout can be done through constraints, autoresizing masks, or by implementing layoutSubview.
Each view controller would instantiate its needed views in the viewDidLoad. View layout can be done with constraints, autoresizing masks, or by implementing viewWillLayoutSubviews.
The use of properties is completely optional. Create public properties for anything to be set/get from an outside class. Optionally create private properties for values internal to the implementation.
Go to the Apple website for Sample Code; download everything that you can for applications that are similar to your goal.

Retain view state upon reloading

I am developing an iPad application that is essentially a sequence of user instructions to mimic a real life system test, with the ability to make modifications on each view if components were to fail (indicating issues that will need to be resolved).
The problem I am having is that the default behaviour of the views seems to be that as I progress forward through the hierarchy, it retains the state of each view, but if I progress back and then move forward again it will have reset the screen.
What I would like to do is have each view save its state, regardless of how the user leaves that screen, so that they can be confident that their work is preserved even if they need to return to a previous step.
Is there any way of doing this? Or do I need to fundamentally reconsider my design?
You need model objects for your views. These could be as simple as dictionaries or as involved as a custom class for each view.
Each view's controller must update its associated model with the changes made via its interface before the view goes off-screen. When it reappears, the VC will update the display with the information from the model.
This follows the dominant Cocoa paradigm of Model-View-Controller (see also: Cocoa Design Patterns); your views display information, your models store information, and the controllers mediate and translate between the two of them.
How to update a model from the view depends heavily on the design of your model. Here's a mockup that may or may not be helpful. All the things named xField are outlets to UITextFields.
// When the view is taken off screen
- (void) viewWillDisappear {
// Assume that when created, view controller is given a pointer
// to the relevant model object (probably by the previous view
// controller)
[model setNameOfHorse:[[self horseNameField] text]];
NSUInteger newBetValue;
newBetValue = [[dollarValueFormatter
numberFromString:[[self betField] text]]
unsignedIntegerValue];
[model setBet:newBetValue];
[model setNote:[[self noteField] text];
}

logic of button to be disabled or not in mvc

Here's an excerpt from a book I'm reading about application design with MVC:
Ideally, the view is so simple and
logic-free as to need virtually no
testing. Users (and developers before
users) can reasonably test the view by
simply looking at the pixels on the
screen. Anything else beyond pure
graphical rendering should ideally be
taken out of the view and placed in
the controller and model. This
includes, for example, the logic that
determines whether a certain button
should be enabled or grayed out at
some point.
what does the bold statement mean to you? what would this look like?
thanks,
rod.
The logic that decides when to enable or disable the button should be residing in the controller and simply calls a method e.g view.EnableContinueButton() to enable/disable the button on the page.
The actual code to enable/disable the button on the page itself should be implemented in the view e.g a EnableContinueButton() method then which calls something like btnContinue.Enable().
Simply put, the view should concern itself with the UI details (show/hide/enable/disable UI elements) and leave all business logic processing to the controller. In this way, the controller does not need to concern itself with the UI elements and the view works independently of the actual business logic.
e.g in the Controller,
public void ProcessOrder()
{
if (!controller.ValidateOrder(model.OrderNo))
view.EnableContinueButton(false);
else
// Process the order
...
}
and in the View
public void EnableContinueButton(bool enabled)
{
btnContinueButton.Enabled = enabled;
}
Frankly I haven't got much experience in MVC (implemented in one project a while back) but I hope the logic separation between controller and view is clear enough.
This is what that bold statement means to me:
The controller is going to be full of nested if statements
The model (or viewmodel) is going to be full of properties to help render the page specific ways, making the object graphs difficult to maintain.
While I think the analysis should not be made in the view, the condition should be set so the button only has to think - show or not show.
eg. only show the examinee details button if the examinee is male.
You either create a viewmodel property ShowExamineeDetails. The view will check if this is ture or not.
the ShowExamineeDetails = is examinee Male?
code should be in the controller.
As for testing, I am yet to find an app that "...needs virtually no testing..."

Resources