I'm trying to figure out how to create a standard look and feel across my iphone app.
So if I ever wanted to change the background for the UIView I would normally do something like this in all my view controllers:
self.view.backgroundColor = [UIColor groupTableViewBackgroundColor]
This becomes quite redundant and error-prone when you have like 50 UIViews to manage. And of course clients change their desired background image every 3 days or so. So my next option is to create helper files, eg:
#implementation GuiDefaultsUIView
+ (void) setDefaultProperties:(UIView *) view {
view.backgroundColor = [UIColor groupTableViewBackgroundColor];
And then manually call [GuiDefaultsUIView setDefaultProperties:self.view];
from each view controller. This works, and it's how I'm doing it now but it means that for every UI object (eg UIButton, UITableView) I'd need to call a similar function for every instance of every class.
What I would like to do is to standardize this so that I get a default look and feel which I can overwrite whenever needed. I've considered Subclassing UIView / UIButton / UITableView but that does not seem like a right way to do it. Adding categories would be nice but I dont think overriding the default methods (eg: init) would be the Right Way to go either.
So. how would you standardize your look and feel?
This is a relatively simple one ;)
You just create a custom UIViewController Subclass named MyVievControllerfor example, and inherit ViewControllers from that class.
Then in the init and viewWillAppear etc. of MyViewController, you can do your customization, just make sure to call super in your subclasses.
You can do the same for UITableViewControllers and even for UIViews, to customize drawing or set standard properties.
We do that in our Apps all the time and it works great.Categories are, in some cases, fine too, for example you can override the drawRect-Method of the UINavigationBar.
What you could do (I've done and seen it in some propjects) is make a class where you store all your constants. Then when you need them just import them and use as appropriate.
Why don't you create a Super class for all of your views?
set all the properties you need there and then make every new view that you create inherit from this super class.
Lets say that you create a UIView called "PreDesignedUIview" that has your design inside like background color etc.
then whenever you create a new view you should set:
#interface NewView : PreDesignedUIview
this will automatically set your design from PreDesignedUIview to the new view.
What is wrong with that ?
Create a Utility class that stores your background colors and images. Then use these methods to get the images/colors. When you are supposed to change, don't go and call different method fro each file, instead change the color that was being returned from the method in Utility class.
Related
What I want to do is a navigation bar with a image on it. I have a tab controller on my main view, and inside each tab I have a UINavigationController. From inside the UIViewController that my tab/navigationController calls, I could set the titleView without much problem, doing this inside the viewDidLoad method:
self.navigationItem.titleView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mylogo.png"]] autorelease];
But, I want to replace all titles in my navigationBar for this view, and it seems ugly to repeat this everywhere. So I did this on the delegate (after linking all the Outlet stuff)
self.tabOneNavController.navigationBar.topItem.titleView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mylogo.png"]] autorelease];
Again, it worked! ok, I'm almost getting there.
But the point is, I've 5 tabs and all of them have navigationControllers inside. I reduced the code repetition from every internal view to only 5 times, but it still. It requires that I do that for the NavController of each tab.
Then I tried to extend the UINavigationBar to create my own, where I could set this in the initializer, and use it in the interface builder as the object class. But it doesn't seem to work. Here is what I did:
#implementation MyNavigationBar
- (id)init {
self = [super self];
self.tintColor = [UIColor greenColor];
self.topItem.title = #"testing please work";
return self;
}
#end
in the interface file MyNavigationBar inherits from UINavigationBar. But this didn't work. Should I overwrite other method? which one? is this a good practice?
I'm not even sure if I should add one navigationBar for each tab, as I said, I have tabs and I want to have a navigation bar / navigate inside them. By now, after a near death experience trying to figure out how the interface builder / outlets and classes work, the code is working, I just would like to make unglify it.
Thank you!
The problem of repeating code which you describe has an elegant solution. Objective-C supports something called a "category", which allows you to add methods to a class. A common use for this is to customize navigation and tab bars. In Xcode 4, you would do something like this to add a category on UINavigationBar:
Hit Command+N or open the "New File" dialog. Next, choose "Objective-C category" from the Cocoa Touch menu:
Click Next and you will be prompted to enter the name of the class that you would like to add methods to as a category. It should look something like this:
Then, you should end up with a save file dialog. A quick note about convention here. Convention is to name a category after the original class, the plus sign, and then a description of what you're adding. Here's what yours might look like:
Once you save your file, you will need get something like this:
Look at that beauty. You can now override the default drawing/init methods as well as extend the functionality of the navbar.
I'd suggest looking into the init and drawRect methods, although I don't remember which ones people use. Also, please note that while under NDA, this may change in iOS 5, so just be prepared for that possibility.
Why not define a UIViewController subclass which sets the title view via self.navigationItem.titleView and have your other view controllers extend from that class? Then you're sharing that behavior across all of your controllers without repeating the implementation.
I've recently started developing for the iPhone and so far I'm doing pretty good but there's this basic pattern I really don't seem to get.
Say, I have a TabBar with two views and a custom delegate protocol, thus my structure is the following:
AppDelegate.h/.m
myDelegateProtocol.h
FirstViewController.h/.m
SecondViewController.h/.m
MainView.xib
FirstView.xib
SecondView.xib
Now I want to achieve the following: I placed a button in the FirstView.xib and I'd like the IBAction which it invokes (inside FirstViewController ofc.) to send a message to the SecondViewController ([self.delegate tellSecondViewContrToSayHi]) and invoke another method which simply prints a log into the console saying "hi I'm here."
So far I know what I need to do in theory:
Specify the protocol.
Implement the protocol in the SecondViewController.
Create an id< myDelegateProtocol > delegate inside my FirstViewController,...AND last but not least:
Set the self.delegate = secondViewControllerObject.
Now, nr.4 is where the problem's at. How on earth do I link the delegate to the other viewController? I mean I'm not the one instantiating the views as the tabBar kinda does that for me,... any advise? Or am I just way too tired to notice a really stupid thing I did somewhere?
Theoretically the same question also applies to the target:action: thing,... I mean, how do I define the target?
Thanks a lot,
wasabi
You have the right idea, assuming that you want relatively tight coupling between these controllers via that delegate protocol.
Since neither controller knows about the other until that delegate property is set you need to have some object which has a reference to both of them wire up that relationship. In your case that's probably the application delegate which can create both controllers, set one as the delegate of the other, and pass both along to your tab bar controller.
What you might actually want is to have the app delegate give both controllers a reference to some shared model object. Your FirstViewController can update that model when you tap a button and your SecondViewController can observe changes to the model to update it's display (or just update its view when it appears based on the current model state). That way your controllers don't need to know anything about each other.
I have been using Objective-C for a while and pretty much understand most of its features. However, the concept of delegates eludes me. Can someone please give a succinct and easy to comprehend explanation of what delegates are, how they are used in the iPhone SDK, and how I can best make use of them in my own code?
Thank you!
There are a couple main reasons to use delegates in Objective-C, which are subtly different:
Enhancing the base functionality of a framework class. For example, a UITableView is pretty boring on its own, so you can give it a delegate to handle the interesting bits (creating table cells, adding text to section headers, what have you). This way, UITableView never changes, but different table views can look and act very differently.
Communicating to parent objects in your dependency hierarchy. For example, you may have a view with a button that the user may push to do something that affects other views. The view will have to send a message to its parent view, or perhaps the view controller, so that it can create or destroy or modify other views. To do this you'd pass the parent object into your view, most likely through a protocol, as a weak reference (in Objective-C, an assign property). The view could then send any message declared in the protocol to the parent, or delegate, object.
This approach need not involve views. For example NSURLConnection passes event back to its delegate, which may be the object that created it, using this mechanism.
Essentially, all a delegate is, is an object that accepts feedback from another object. Put simply, when stuff happens to an object, it tells its delegate (assuming it has one).
For instance, lets say I have a UIViewController with a UITextView placed in the middle of the view. I set up my UIViewController to be the delegate of the UITextView. Then, when certain actions are performed on the text view (begin editing, text changes, end editing, etc), it tells it's delegate so it can do whatever logic it needs to do, like spell checking every time characters change, or dismissing the keyboard when it receives a return key press.
Delegate methods perform a similar function to callback functions in C.
Hope that makes sense :)
Best and simple concept I got from a Lynda.com Tutorial was: When you set a Delegate it means you have been given work to do. So, if you want to use methods that are written in a protocol method, you must implement them by searching in the Delegate Class Reference and using them. I hope it helped.
By the way, Delegates are excellents. They are your friends. They have been made to make your life as a programmer much easier.
I have a UIViewController with a XIB and want to add programmatically another subview.
During the initialization of the subview (initWithFrame) i want to set some attributes to values according to attributes that belong to another Object which holds data (actually a ViewControllers Child-Object, but not a view).
-(id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// The following is kind of what i want
self.myAttribute = [self.viewController.otherObject otherValue];
}
return self;
}
I want to conform to the Model-View-Controller paradigm and try to seperate my data from the subview and don't know how to access the data from within the subview.
Thanks in advance for any answers and comments how to improve the question.
EDIT: All three answers are useful for me to understand that my design is somehow the wrong way of doing the thing. As far as i understand the subview properties should be modified by the controller instead of trying the subview making to get the information. I will accept Jasons answer for his effort explaining this to me.
If you put this view in place using something like a UINavigationViewController you can use the parentViewController property. If not--and really, just in general--you can create properties that need to be set on your new view controller, and just set them in the parent or whoever else might create it.
Getting data passed around a view hierarchy can be tricky. You have a few options for global-like data:
Actual global variables (hosted in one .m file and declared in a shared .h file). Not recommended, except in rare cases where you have e.g. static data that the following approaches seem silly to use with.
A shared (singleton) controller object that owns the shared data. Then you could do, say, [[AppController sharedController] otherValue] and access it from anywhere in your application. This is good for what you might call overall properties or settings across your application. You wouldn't use this to pass around view-specific information, generally.
If the data is view-specific, you might have it "ride along" with your view controller hierarchy, by passing it from one view controller into the next as you create and push the controllers. Then when you create the views themselves, as above, don't look for the property in the initWithFrame method, but set up a property on the view that you can set to push in the data immediately after creating the view.
A simple solution for the general problem of initializing a subview with attributes is to write a custom initializer in your subclass.
-(id) initWithFrame:(NSRect) aFrame andAttribute:(SomeClass *) anAttribute{
if (self=[super initWithFrame:aFrame]) {
self.attribute=anAttribute;
}
return self;
}
You would initialize the object like so:
MySubviewClass *msc=[[MySubviewClass alloc] initWithFrame:frame andAttribute:[self.viewController.otherObject otherValue]];
This will work fine if your talking about a subview controlled by the the same controller i.e. it is a subview of the controllers.view. If you loading another view, then you need to go the data-model/navigation-controller route.
In Objective-C, I have a category for a class:
#interface UILabel(CustomInit)
- (id)initWithCoder:(NSCoder *)coder;
#end
What I'm doing is writing a custom init function that does some extra stuff, and what I'd like to do, is in this custom init function, call the UILabel's base initWithCoder. Is this possible? How so?
EDIT
Thanks. Ok, so my plans moot. Can't just overload initWithCoder. Is there a way to achieve the same functionality (where all UILabels get this added initialization step) without overloading initWithCoder? Or perhaps is there sample code for the UILabel's initWithCoder that I can just rewrite with the added code?
EDIT
Ok, so to be clear about what I'm trying:
Can I embed a custom font in an iPhone application?
has an answer in which someone manually adds a custom font on the iphone using the private GraphicServices function GSFontAddFromFile. I tried this code and it worked great for manually setting the font of a label. However, if you try setting the font in Interface Builder, it doesn't load properly, it just drops down to the system font. What I wanted to do was load the font manually and set the label's font automatically with the chosen font in IB. This way I don't need to make an outlet for every label I put down. I also don't have to write a ridiculous label subclass (which was also suggested in that thread and does a large amount of custom drawing) which I found rather grotesque. Now I could still make a subclass for all my labels, but then there's the case of embedded labels in other UI objects, ie UIButtons. I'd like the embedded labels to also not be broken.
Any suggestions would be great. Thanks.
From the Mac OS X Reference Library:
When a category overrides an inherited
method, the method in the category
can, as usual, invoke the inherited
implementation via a message to super.
However, if a category overrides a
method that already existed in the
category's class, there is no way to
invoke the original implementation.
How do you guys feel about this?
Grab the original method address for initWithCoder at runtime and store it in a static variable. Do a method swizzle on it to replace the classes implementation with the my initWithCoder. And then in my initWithCoder, I would call the original method stored in the static variable.
You can put it in a category and call this class initialization step at the start of the program, making sure it can't be called twice, or if it is it does nothing.
It seems dangerous, but I feel like it should work.
Method swizzling should work as kidnamedlox suggested .
Your exact same question was discussed in this Stanford itunes class by Evan Doll
https://podcasts.apple.com/us/podcast/iphone-application-programming-spring-2009/id384233222