I need to create an application that should have several themes. It means that for each theme we use different set of images. The theme should be changed without full reload of all view controllers (just change the images). The themes could be added via In app purchases so they should not be hardcoded.
I also want to use Interface Builder to create my views.
Question: what is the best way to do that?
On MyAppNotificationUIThemeChanged every controller has to retrieve and set images for every UIImageView in it's main view. I can think of 2 approaches for doing this:
1) Brute force. Something like this:
self.imageView1.image = [self currentThemeImage:#"someController_image1.png"];
self.imageView2.image = [self currentThemeImage:#"someController_image2.png"];
2) Automated approach.
NSArray *imageViews = [self fetchAllImageViews];
foreach (UIImageView *iv in imageViews) {
iv.image = [self currentThemeImageForTag:iv.tag];
}
where currentThemeImageForTag: is something like this:
- (UIImage*)currentUIThemeImageForTag:(NSUInteger)imageTag {
return [self currentUIThemeImage:[NSString stringWithFormat:#"%#_%u.png", NSStringFromClass([self class]), imageTag]];
}
I also suggest to use NSBundle to package theme images and other resources.
Update:
I chose NSBundle only because of it's cool methods like pathForResource:ofType: and such and also ability to support localization. You can create bundle with XCode (just look under Mac OS X -> Framework&Library section in new project dialog). Put your images in Resources, remove liks to any frameworks (there is no code anyway), build and that's it. In your app load bundle with [NSBundle bundleWithPath:pathToDownloadedBundle]. Beware though: I don't know if Apple allows to download bundles and use it's content in app. All I can guarantee is that it works on simulator.
I would suggest the approach that I detail in my answer here:
Is there a simple way to set a default font for the whole app?
You could have view controllers in your app ask a ThemeApplicator class to style them when necessary. It would do this by setting the correct background image, that sort of thing. You can still use IB to build your views; just don't set a background in IB (or set a default background). The key is that you programmatically update the background later.
Look at Three20 project, especially at Three20Style.
Related
I created a new project "Single View Application" and designed the mainView with Storyboard. My main view contains a UIButton that opens the camera, the camera scans barcode and automatically goes to a website. Now I created a webView programmatically so that website can open and also created a UIButton inside the webView. Now I want that UIButton to act as home botton and return to mainview. I am unable to do that, please help.
ViewController.m code: http://cl.ly/FKj8
My storyboard looks like:
You really should look into the View Controller Programming Guide -- by switching around the contents of a single view controller, you're making a lot of extra work for yourself with little benefit. By using multiple view controllers when you want to have different "screens" in your app, you can take advantage of storyboarding for easier development, and you automatically get better memory management (read: less potential for crashes), too.
However, to more directly answer your question... if you're putting the WebView into the view hierarchy with [self.view addSubview:webView], you can remove it with [webView removeFromSuperview]. (This means you'll have to keep a reference to the WebView around so you can refer to it when you want to dismiss it.)
I also noticed in the code you posted to cl.ly an unrelated method -deviceModel which uses uname() to get device information. This is a bad idea, for two reasons:
uname() isn't guaranteed to do something useful on an iOS device (even if it currently does). Use the UIDevice class instead if you need this kind of info, or...
Generally, you don't want to test for the device name to enable functionality in your app; instead, you should test for the capabilities you need. (For example, if you look for a device name starting with "iPhone 4" to test for a Retina display, you'll miss the 4th-generation iPod touch, and the iPhone-5-or-whatever-they-call-what's-next. Instead, use the UIScreen class.)
You can use a device modifier (i.e., ~ipad) to provide a device-specific key in Info.plist, and to specify a device-specific launch image (Default.png for iPhone, and Default~ipad.png for iPad, for example). Those two things are specifically mentioned in Apple Docs, but they don't say that this will work for any other kinds of files.
I've discovered (quite by accident) that this works for loading .xib files via initWithNibName:bundle:. So for example, I can have MyView.xib and MyView~ipad.xib, and this code:
MyViewController *viewController = [[MyViewController alloc]
initWithNibName:#"MyView" bundle:nil];
... will totally load MyView~ipad.xib on an iPad, and MyView.xib on other devices.
So, 1) Is this documented somewhere? I sure couldn't find it any any Apple docs. It's sure handier than checking UI_USER_INTERFACE_IDIOM() and hardcoding two different nib names everywhere, but I kinda don't trust it if it isn't documented.
And, 2) Does anyone know what version of iOS this started working in? I've only tried it in 4.2, and it works there. Device modifiers in general (even for the documented things listed above) are 4.0 minimum.
I had this same problem. The answer didn't make sense at first, but the good news is that it's easy to do! :)
Just name your iPad xibs without any modifier and your iPhone xibs with ~iphone modifier and it'll select them correctly.
So, with MyViewController, you'll have MyViewController.xib for the iPad and MyViewController~iphone.xib for the iPhone. Then you can just init your view controller with simple alloc/init.
[[MyViewController alloc] init] and it'll grab the right xib.
So, when I create a new view controller in XCode, I always choose the box to format it for ipad, because the xib it will create will be named MyViewController.xib and you want that one to be the iPad sized xib. Then I create a second xib, formatted for iPhone and name it with the ~iphone modifier.
The documentation is a little contradictory at times, but this page talks about how resources with an identifier will default to iPad.
ImageSoundResources
Check the section about using high res images. I know we're talking xibs and not images, but it does work. My last 6 apps have all used this idiom.
Actually, it is explicitly defined in the docs, but as a footnote.
CocoaNibs
In the note at the bottom of "Loading NIB files using NSBundle":
Note: If you are developing a
Universal application for iOS, you can
use the device-specific naming
conventions to load the correct nib
file for the underlying device
automatically. For more information
about how to name your nib files, see
“iOS Supports Device-Specific
Resources.”
Which links to Cocoa Conceptual LoadingResources
However, yes, this is a 4.0+ only feature.
I hate to be that guy and answer my own question, but I think the answer is:
1) Nope, not explicitly documented in any Apple documentation, and
2) 4.0 and higher (this based on my own testing)
All you really save is a couple lines of code checking for UI_USER_INTERACE_IDIOM(). Still, I'll take it. Less code is less code.
The appropriate technique to use in iOS 3.2 and later is the UI_USER_INTERFACE_IDIOM() function. I typically use a ternary operator to init the UIViewController with the appropriate XIB.
UIViewController* controller = [[UIViewController alloc]
initWithNibName:UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ?
#"YourViewController-iPad" : #"YourViewController" andBundle:nil];
k, I'm new to this so apologies all around, generally.
I'm trying to access the UISwitch value (on or off) from a different view and class and can't make it work.
It's a simple 2 view app. Main view and the second is a preference menu.
trying to write an if/else method to play sound when the switch (on the other view) is on and not when its off.
I cant seem to make it work. Any thoughts or some syntax examples would really help me out.
Thanks.
As Matt Wilding said "it's not good form to access UI components of one view controller from another...".
Instead of accessing the view object, when the switch state is changed by the user you save the status into NSUserDefaults as preference value. Whenever you want, you can access the switch status value through the preferences.
I'm going to take what I think you're trying to accomplish here and suggest an alternative approach. You want to have a preference in your app (assumed from "preferences menu") that allows the user to set something like whether or not you app plays background music. (May not be exact, this is just for clarification).
Typically, in a well designed app, the flow is driven by the data, with the UI reflecting the state of the data model and the controllers coordinating the two layers. What you are suggesting is to have your application play music based on the state of the UI, which is not backed by any data model. This cuts out the model level, and as you noticed, can lead to awkward attempts at communicating between the UI of different controllers for information.
Things like application preferences are typically stored in a nifty .plist file that is managed through the NSUserDefaults class. This would be a great place for the data level tracking of your preference. In this situation, the UISwitch would represent the state of the flag in the settings file, and changing the value of the switch would change the value in the file. Anywhere else in your application that you need to know if the play-sound-flag is set, you reference the data model info instead of the UI. This decouples the view controllers from each other, which is a good thing.
For this purpose add selector for swith and make NSInteger property in app delegate.Like the followed
[
yourSwitch addTarget:self action:#selector(switched:) forControlEvents:UIControlEventValueChanged];
-(IBAction) switched: (id)sender
{
int state=0;
if(yourSwitch.on)
state=1;
else
state=0;
objAppDelegate.switchState=state;
}
then you need to access this appDelegate property in second view where you are playing sound
then according to this value you can do what you want and for making object of appDelegate class you need this line
YourAppDelegateClass *objAppDelegate=(YourAppDelegateClass *)[[UIApplication sharedApplication] delegate];
ok if you have any other doubt then you can ask.
I am trying to develop an email-sending app which sends messages via custom API. The problem is that UITextView does not allow to display html content, and in order to forward/reply messages properly you sometimes need to edit an html-content that came from a previous message. However in a standard Mail.app on iPhone the UITextView or whatever they use there perfectly displays colored-text and links. My question is: is there any ready/half-ready component to recreate this behaviour, or at least any opinion on approach how to do that would be great. Thank you.
EDIT: MFMailComposeViewController does not fit the purpose of the application as it does not allow to control its behaviour and does not allow to send messages via custom API.
TTMessageController from Three20 is what I use now, it allows to customize most stuff, except - the message body text view is just UITextView - and I can not put any html-content in there.
Three20's message controller is the closest you're going to get to a lookalike compose view.
Beyond that, you actually have two problems: autodetection of "live" content (links, phone numbers, etc) in text, and the rendering into a text view of arbitrary HTML.
For the former, UITextView has a dataDetectorTypes property on it. If you set that appropriately, you should get automatic detection of links, phone numbers, etc.
However this doesn't get you the latter. There's no trivial way I know of to convert HTML to an NSAttributedString, say, which would be how I would think of approaching it, and a totally cool project for someone to do. Maybe someone else has a resource on that.
That interface is ready for you to use. You can use it as a modal view, like this:
- (void)replyAction
{
MFMailComposeViewController* mailController = [[MFMailComposeViewController alloc] init];
mailController = self;
[self presentModalViewController:mailController animated:YES];
[mailController release];
}
You also need to import the MessageUI at your header file:
#import "MessageUI/MessageUI.h"
Set the inheritance:
YourClass <MFMailComposeViewControllerDelegate>
and add the MessageUI framework to your project. To do that, right click any group (like the Resources one) and choose Add -> Existing Frameworks... Then, look for MessageUI.framework. Add it and you're done.
Don't forget to check the official documentation for a lot of other options you can set for the MFMailComposeViewController.
After reading the iPhone Human Interface Guidelines, I notice there's little mention of checkboxes in the style that one may encounter them on the desktop or web.
Checkboxes are generally handled by UISwitchs on the iPhone, but for an app I'm presently working on, they're really not the right control. Instead, the control you'll see in Mail is a much better fit:
Actual mail blanked out. Obviously.
How would I go about using these checkbox controls in my app? Are they standard, or will I need to imitate them with a custom control?
Cheers friends.
You'll need to create a custom control. It won't be difficult since UIControl already has 'selected', 'highlighted' and 'state' properties at your disposal. You'll just need to draw and toggle appropriately.
Don't subclass UIControl. What you want is a UIButton of "custom" type. Load it with your "unlit" image in IB (or programmatically in -viewDidLoad--you can set it appropriate to its data there too, if you came here with that property already "checked").
Point its touchUpInside event at a method called -(void)toggleCheckBox, and in that method, toggle whatever setting you're toggling (probably a BOOL property of the objects you're listing), and toggle the "lit/unlit" status of the button image by using its -setImage: forState: method. Use the control state UIControlStateNormal.
I do something similar where I let people poke a button to toggle the "favorite" status of the thing ("thisEvent"--a member of an array of local cultural/arts events) they're looking at:
- (IBAction)toggleFavorite {
if (self.thisEvent.isFavorite == YES) {
self.thisEvent.isFavorite = NO;
[self.favoriteButton setImage:[UIImage imageNamed:#"notFavorite.png"] forState:UIControlStateNormal];
}
else {
self.thisEvent.isFavorite = YES;
[self.favoriteButton setImage:[UIImage imageNamed:#"isFavorite.png"] forState:UIControlStateNormal];
}
}
I'm pretty certain there is no standard way to do this. However it's fairly simple to achieve, all you need is two images, one for each state. I would probably do something simple like subclass UIImageView and add a setState:(BOOL)theState method, which would then simply select the relevant image.
I'd rather subclass UITableViewCell then UIImageView. UITableViewCell allready comes with selected/unselected states and handlers for editmodes etc.
As said before, you'll need to subclass UIControl. The actual process was discussed here w little while ago.
I also found a description of another way to do this using the same image/method that the Mail app uses:
http://networkpx.blogspot.com/2009/07/multiple-row-selection-with-uitableview.html
but as this implements undocumented features of the iOS SDK, it may not be best for apps intended for the official App Store.