Now, I know this question is very opinion oriented because people generally design their apps differently... However, I want to know if I'm doing something incorrectly, or generally frowned upon in the objective c programming community, or if you have any suggestions as to how an experienced obj c developer might design this differently...
LOCATION is a struct and BULLET is an enum defined in a Constants.h header file, BulletV is a subclass of UIView, and Bullet is a subclass of NSObject
I have the following code:
#import "Bullet.h"
#implementation Bullet
#synthesize Type;
#synthesize Location;
#synthesize Strength;
- (id)initWithLocation:(LOCATION)location Type:(BULLET)type Strength:(int)strength
{
self = [super init];
if (self)
{
self.Location = location;
self.Type = type;
self.Strength = strength;
}
return self;
}
#end
#implementation BulletV
#synthesize Type;
- (id)initWithLocation:(LOCATION)location Type:(BULLET)type
{
self = [super initWithFrame:CGRectMake(location.x - 10.0, location.y - 2.0, 20.0, 40.0)];
if (self)
{
self.center = CGPointMake(location.x, location.y);
self.Type = type;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
switch (self.Type)
{
case Normal:
self.backgroundColor = [UIColor redColor];
break;
default:
break;
}
}
#end
Now, I am relatively new to MVC.. but it seems like keeping the model and the view separate creates a whole lot of duplications, which i hate in my code..
For example, the fact that we have 2 of the same named Properties (Location and Type) utilized in these implementation blocks kind of bothers me, but I don't know how I would do this any other way while still maintaining separation between the model and view.
Also, both init methods for the view and model are very similar, with the exception that the model also includes a strength parameter..
I have a very java-oriented mindset when programming, I like to have individual files for each object, and for each object to have a state and certain actions.. Now, this new design disallows for any actions to be utilized in these headers, because they need to be placed in a controller.
Any comments are welcome, if this is the way you would make a "bullet object" (more like just a rectangle) in your app, then feel free to comment so, but it just doesn't seem right to me. Feel free to give your insights.
I immediately see a couple of things I would change.
First naming conventions. Properties and instance variables usually start with a lowercase character while classes start with an upper case character. Class names should be descriptive so BulletV would be clearer as BulletView, "BulletV" might be a "BulletView", a "BulletVelocity", or who knows what else.
Secondly I would not allow a view to be responsible for it's own position. A view should be responsible for it's contents but not need to be aware of it's position in or anything else about its superview.
Instead I would consider what sort of object contains bullets with positions; a room, a level, or whatever. Let a view for that space position the views for whatever bullets it contains. An individual BulletView only need to decide how to draw itself based on the type of its bullet. Which, as Jano suggested, probably means you want to pass a bullet to the view's designated initializer.
Once you have that separation of responsibilities it hopefully makes sense for whatever object models the space containing bullets to manage their positions and possibly other behaviors like creating and destroying bullets.
If you were going to reuse OrangeView you would name it FruitView to start with, and pass a Fruit object superclass which would have a method fruit.color instead using a switch.
But I see what you are doing, color is a visual property, and MVC dogma demands that you keep each part isolated, so you smuggle Bullet in little pieces. This is not very object oriented, or any easier to understand or test. A benefit of OOP is thinking in objects, if you were to show your code to yourself, you would understand it better passing a Bullet object.
edit
All problems in computer science can be solved by another level of indirection except for the problem of too many layers of indirection. In a small app is very common for C to know M and V, and for V to know M. In java server side you create a bean just to feed the view, but that's uncalled for in such a small example. Even when you can spot the pattern before opening the class, it's more effort, more classes, more indirection. Not always a good fit for every application.
Apple talks about it in Cocoa Core Competencies and Cocoa Fundamentals Guide.
One can merge the MVC roles played by
an object, making an object, for
example, fulfill both the controller
and view roles—in which case, it would
be called a view controller. In the
same way, you can also have
model-controller objects. For some
applications, combining roles like
this is an acceptable design.
Related
I'm pretty new to this and I don't completely understand all of the inner workings of Objective-C, so I was hoping someone could help me out.
Essentially I have a Person class, where each person is represented by a group of objects (a button, label, and image view) that appear and disappear at certain times throughout the implementation. Occasionally, I want the person to move, and the best way I have figured out so far is to treat all three objects separately and animate them at the same time.
I figured there must be a better way to do this, and I am wondering if it is possible to create something like a "subview" (I don't know if this is the right word for it) and insert the button/label/imageView onto that and then animate the subview. I would imagine it would be something like:
Person *person = [[Person alloc] init];
person.view.frame = CGRectMake(x, y, width, height);
[self.view addSubview:person.view];
But this doesn't seem to work. Any suggestions?
Thanks in advance!
OK so I'm afraid you need to change your design a bit. Apple work on an MVC architectural design which is really important to adhere to when it comes to making Apps.
What you've got here is a mixing between the model and view which is exactly what isn't supposed to happen with MVC.
Essentially you've got:
1) Model layer which is where you have all your data and objects for encapsulating information, just like your Person class.
2) View layer which is for controlling and defining UI elements of your apps like UIView, UIImageView e.t.c.
3) Controller layer which is the interface between Model and View layers. This include classes like UIViewController which manages a UIView.
In your situation you need a UIView into which you can put all the relevant UI (buttons, images) that tell the user about a person. Then you need a Person class. Probably a subclass of NSObject that defines the information for each Person in your app e.g. maybe a UIImage property that can be displayed in a UIImageView. And then a controller (I would recommend UIViewController) which would instantiate the relevant Person objects and then update the UI as required.
I'm not entirely sure what you want in terms of animation but you can look at this for information on how to animate views. And check out:
[UIView animateWithDuration:1
animations:^{
// Cool animations here...
}];
I would get to grips with MVC and other key concepts before getting too snazzy though...
I'm coding an app, and have several views that are reused (f. ex. containing a textfield, a label and a button)
Now i would like to just create it in a "globalviews.m" file once and add it as a subview to the mainview. (I really don't like the interface builder)
I could easily create a function that returns a UIView, with the components in them, but i would like to access the controls of course.
I was hoping something like (making "searchview" global in the viewcontroller in use)
// making searchview a global thingy
UIView *seachview ;
// rest of code here and then in viewdidload:
UIView *seachview = [[UIView alloc] init] ;
searchview = [[globaviews alloc] thesearchviews_name] ;
[self addsubview:searchview] ;
But how could I make controls inside easily accessible. Like:
NSString *something = searchview.textviewname.text ;
Or would this be a terrible idea to begin with?
Its just the way I would prefer to code...
You can create a custom class that is a subclass of UIView. You could then add properties for each control (the same way you would add NSString, NSNumber etc). Or you could create public methods to modify / get data.
e.g.
- (void)setTextFiledColour:(UIColor *)color;
- (NSString *)getTextFieldText;
My personal opinion (from a lot of experience) is to learn interface builder and deal with it. It is perfectly possible to do what you want and many people agree with you and choose to do it that way. Personally I've never seen it done "right". Meaning that its all done custom to create their own patterns and methodologies, avoiding years of experience and testing that has gone into the patterns provided by interface builder.
I find that storyboards in particular force a very specific pattern and style of coding. I think moving away form that is a huge mistake, as if used correctly it has great potential to abstract away UI / Code, prevents taking shortcuts that come back later on and most importantly when someone else needs to change it, there is no ambiguity, you can't make a mistake with class names or variable names etc.
I've used storyboards a lot and have yet to have an issue with them, on the flip side i've worked with developers who insist on doing it all by hand and have never had so many issues, and shocked at how long it takes to develop applications in this manner.
In the end its up to you.
Note
You mentioned wanting to create a view and reuse it. Its also possible to do this in a .xib file and reuse it that way. Losing some of the storyboard abilities but might give you a little of both worlds.
Very frequently we reuse same view controllers when developing universal apps both for iPhone and iPad. But frequently some customization is needed, like:
IF iPad THEN
...
ELSE
...
So, in order to achieve such customization the controller might have some property that is set during initialization of the controller, or there might be custom constructors. Just curious is there design pattern that suites for such situations.
Don't.... :) Use a common class called for instance MyClass and then sub-class it MyClass-iPad & MyClass-iPhone and use two different XIB for each. Avoid using this kind of stuff (there is no need for it).
Explanation:
The iPad version should only be aware of classes of the type Something-iPad this makes the code clean and creates a well defined architecture. If I jump into your code and someone tell's me: "Ok Jacky Boy, you have to make changes on the iPad version". I won't care to look ath the Something-iPhone classes. Most of the logic (business) should be on super class Something where the small tweaks should be on the sub-classes.
On side note, on most of my projects, normally I don't have anything on the Something-iPhone classes, because the design is done on the XIB. On the Something-iPad I would normally keep a reference to a UIPopOverController (just an example) and some rotations tweaks.
Edit 1:
I would receive an NSDictionary on the init of the UIViewController, like this:
initWithNibName:bundle:configurationDictionary:
After receiving this configurationDictionary, I would then use it on the viewDidLoad (for example). You could then do some cool stuff like this:
- (void)viewDidLoad
{
[[self view] setBackgroundColor:[[self configurationDictionary] objectForKey:BACKGROUND_COLOR_KEY]];
}
If you have different initializers or larger chunks of different functionality then it makes sense to define a base class with the core functionality and then an iPad-specific subclass and an iPhone-specific subclass.
But in cases where you only have a trivial difference (for example, displaying an action sheet), then I would simply use something like:
- (void)someMethod {
// a bunch of stuff that is the same
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// one or two lines for iPad
} else {
// one or two lines for iPhone
}
}
I have plenty of situations where I do both - subclass for the bigger differences and use UI_USER_INTERFACE_IDIOM for trivial differences in the base class.
If you need the same VC in N places but each time initialized a little differently I would move the logic in a specific factory class / category on the VC
like its done with for example SLRequest objects / in ShareKit
I am going to add 50 windows with 4 UIButtons (with text) and 1 UILabel (with text as well) to EVERY window. Do I need to do it this way? Is there a better way?
It's never a good idea to create additional windows if you can help it, especially on iOS where there is almost never a need to do so. You should create a UIViewController subclass and make it your root view controller (this is already set up in the single view application template). Then, make a subclass of UIView — let's call it "MyQuizView." "MyQuizView" should have a custom initializer that takes five NSStrings (one for the question, four for the answers) and an integer to determine which answer is the correct one. The UIViewController subclass can then instantiate 50 of these views handing them values from the model and make them its main view's subviews.
EDIT: Here's an example of a custom initializer for a UIView subclass.
- (id) initWithFrame:(CGRect)frame question:(NSString*)ques answers:(NSArray*)ans correctAnswer:(int)correctAns{
self = [super initWithFrame: frame];
if (self) {
self.question = ques;
self.answers = ans;
self.correctAnswerNumber = correctAns;
[self setup];
}
return self;
}
A custom initializer starts with init. It sets self to the return vale of its superclasses' designated initializer, then, if self is not nil, it initializes its state–usually using the arguments passed to do so. At the end it returns self. This one assumes you have the correct properties and calls a method called setup after setting the properties to the correct values, allowing you to use them to create labels and whatnot. Alternatively you could take the values passed in and use them to immediately create the labels and buttons, set up the target actions and place them as subviews, that way you wouldn't need to keep the arguments as properties. Each button can be given a numerical tag so that you know whether or not the answer was correct or not (based on the integer passed into the initializer, which you would have to store somewhere). This is all from memory, but hopefully it's correct.
Don't waste your time creating 50 windows (if this is a MacOS question) or views (if this is an iOS question). Wow, that'd be awful.
Instead, create one single view which has four buttons and at least one label.
You can then populate the string values for each of those items from your list of questions & answers. You can keep those questions either in a plist file or a CoreData database or some parseable flat file, etc. Connect the four buttons to the (game?) controller via "IBAction" methods.
Let's say I have a two subclasses of UIViewController called MasterViewController and DetailViewController.
DetailViewController has a property of type NSNumber called level and a UILabel called levelLabel.
MasterViewController has a segue to DetailViewController called ToDetail. MasterViewController's prepareForSegue is like so
- (void)prepareForSegue:(UIStoryboardSegue)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ToDetail"]) {
DetailViewController *detailVC = (DetailViewController *)segue.destinationViewController;
detailVC.level = [NSNumber numberWithInt:10]; // never mind the literal...pretend there was some algorithm for it
}
}
So then, in DetailViewController we implement the setter for levelLabel like so:
- (void)setLevelLabel:(UILabel *)levelLabel
{
if (levelLabel) {
_levelLabel = levelLabel;
_levelLabel.text = level.stringValue;
}
}
Is this good code design? Also, could you critique my code writing style? I pretty much wrote all this code on the fly so this is pretty much how I write code for the most part.
I thought of this question while showering because this is how I implement the setting of almost all the label texts that depend on a segue.
What follows is my own way of thinking about such relationships. Italics applies to your question.
You have the thing being controlled (the label) the controller (destination view controller) and the context it is being controlled within (the source view controller). This can also be expressed as model-view-controller, but I think thinking about a context can apply to much more specific and localised situations.
You should generally try to keep information flow going in one direction, from the context downwards. Objects should not have to be aware of the context in which they exist, ie they shouldn't have to ask for any information, they should be told everything they need to operate. So the source view controller should push the level to the destination view controller, the destination view controller should push this information to the label. This is what you already have, sort-of.
To build upon the above, not only should information flow in one direction, but I also try to ensure the relationships are causal, ie pushing information from one object to another should cause it to subsequently be pushed to the next object. Your code is not doing this which is probably why you have a bad feeling about it.
A more appropriate thing to do is set the text property of the label within the level setter, so that when you set or change the level, the label will update subsequently. The label may or may not be loaded so you will have to check whether it is using -isViewLoaded; -viewDidLoad is the appropriate place to set the text property upon first load.
(When I say 'push' that's just my way of thinking about setting properties or passing arguments because it implies directionality. It is really dependency injection. An example of pulling information would be delegates and data sources. But note here still the object isn't aware of any context, delegates and data sources are clearly defined as protocols, not classes, and usually within the same header file, and are themselves pushed onto the object from a surrounding context. So yes the object is asking for information, but on its own terms and from a system it has no knowledge of.)
Re coding style:
That's exactly how I write code but note Apple reserves the use of underscore prefixes