When I am developing apps in iOS before, I always put the image sources of the app at the root directory of the project, and then I will load them using [UIImage imageNamed:#"image.png"]. I did't even include various sizes and resolutions such as image#2x or image#3x. But I am afraid this is not a good practice, because I cannot deal with different screen resolutions.
Now, I want to make use of the Images.xcasset to store all my image sources so that I will be able to load just the bundle name, and hoping that the system will pick the image with the right resolution automatically. Therefore, I made a test and place my image set under Images.xcasset and named it as images, and then in my UIViewController.m I am trying to load the image by calling [UIImage imageName:images]. As a result, it didn't work. So I searched for an answer and found out that I should call [UIImage imageName:#"images60x60#2x.png"] in order to load the the 60pt #2x image.
But I think this still did not solve my problem, because I am still choosing which image to load. Is there a way to load the entire image set or load the image according to the resolution of the screen?
Please help. Many Thanks.
EDIT: Added Screen Shots
I use AppIcon as a test
As you said at last that you are using AppIcon as a test. Please don't AppIcon it is made for internal use. try to create you own imageSet and use that. It should work.
Two things :
As said Ankit, do not use App Icon, use your own set
do not use a file suffix. Just reference [UIImage imageName:#"MyImage"] (not [UIImage imageName:#"MyImage.png"])
I have a custom Resource bundle where i'll have all my images and localization files inside it.
When I assign some images in xib I have to type the entire path where image resides inside my bundle for example MyResource.bundle/Images/Event/xxx.png.
Which means in xib it always taking apps main bundle for images.
Here my question is is there any possibility is there like I can change the default bundle preference to MyResource.bundle/Images So that I can see the image in xib when assigning to a uiimageView or any other.
preference? in simple words its not possible because there is no property like that, so you need to find out a way to achieve this, but i have used bundle for resources but not have this kinda problem because bundle is logical path
My solution is add one image manager to manage your image resource. I mean when you need an image, you just call your manager's method passing in the image name. And your image manager will read the correct image file from corresponding bundle and return it based on device's language.
i am using XCode 5 when i am naming the images like that :
fbicon~ipad.png
fbicon~ipad#2x.png
Xib is getting the image
but when i am using this Convention
although tha naming convention is
fbicon~ipad.png
fbicon#2x~ipad.png
xib is not picking the images in that case.
Dont Know Why?
Migrate to use the assets catalogue. You just drag your images into the image wells and interface builder picks them up.
If the catalogue doesn't show the iPad iphone specific wells, open up the properties right side bar and tick the appropriate boxes.
assets catalogue
This is happening since you are using XIB.
When used with XIB, for images to be used on iPhone one must use the convention "image~iphone" and for iPad you have to use "image~ipad". #2x is automatically appended based on which version (retina/non-retina) of the image is needed. That's why the first convention works for you.
In case of getting images from the code, e.g. if you have following versions of the "image"-
image~iphone.png
image~ipad.png
image#2x~iphone.png
image#2x~ipad.png
You just call-
[UIImage imageNamed:#"image"]
In this case, the second convention that you mentioned works- as detailed in the Apple doc-
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/LoadingResources/Introduction/Introduction.html
So, nothing really wrong here as I see. For XIBs, the first convention is correct. For using with code, second convention is correct.
I'm using Xcode 4.2 and in the process of writing a universal app. I selected SingleView Application template when starting with a new project. XCode added ViewController1.h, ViewController1.m, ViewController1_iphone.xib and ViewController1_iPad.xib. I need to add more UIs and clicked on the File...New...New File and selected UIViewController subClass template and seeing two checkboxes (Targeted for iPad, With Xib for User Interface).
What should I do here to support both iPad and iPhone while at the same time have a common .h and .m files that share the same code. Do I need to add code to check whether it is a iPad or iPhone by doing this in my view controllers?
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
} else {
}
Also, I have seen people talking about ~iPad and ~iPhone. What is this all about?
If I understand correctly, do I have to design the UI separately both for iPad and iPhone due to different screen sizes?
I'm totally confused here.
Please help.
You can either add two nibs (one for ipad and one for iphone), or you can add one nib that will properly scale for either interface. Normally you'd add two nibs if you're making a view that will cover all or most of the screen, and you'd add one nib if you're making something small that will, perhaps, be fullscreen on iphone but displayed in a popover on ipad.
The tilde suffixes ~ipad and ~iphone are described under the heading “iOS Supports Device-Specific Resources” in the Resource Programming Guide. Notice that the suffixes are entirely lower-case, not camel-case as you wrote in your question. This matters because iOS uses a case-sensitive filesystem.
When you get a path for a resource using an NSBundle message like -[NSBundle pathForResource:ofType:] or -[NSBundle URLForResource:withExtension:], iOS will first look for the resource file with a suffix of ~ipad or ~iphone, depending on the current device. For example, suppose you do this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"setup" ofType:#"plist"];
If you run this on an iPhone-type device (including an iPod touch), or on the simulator in iPhone mode, iOS will first look in your app bundle for a file named setup~iphone.plist. If it finds such a file, it will return the path of that file. If it doesn't find that file, it will instead return the path to setup.plist.
If you this on an iPad-type device, or on the simulator in iPad mode, iOS will first look in your app bundle for a file named setup~ipad.plist. If it finds such a file, it will return the path of that file. If it doesn't find that file, it will instead return the path to setup.plist.
All of the other APIs that get resources from bundles are built on top of NSBundle, so they all benefit from this device-specific lookup. That means if you use +[UIImage imageNamed:], it will automatically use a device-specific image, if you have one in your bundle. And if you use -[NSBundle loadNibNamed:owner:options:], it will automatically load a device-specific nib (.xib) file, if you have one in your bundle.
This simplifies your code, if you use the suffixes. If you create MyViewController~ipad.xib and MyViewController~iphone.xib, your app will automatically load the correct one for the current device. You don't have to check the user interface idiom; NSBundle checks it for you. (You could also use the names MyViewController~ipad.xib and MyViewController.xib and get the same effect.)
Now, you may have noticed that when you created your “universal” project, Xcode gave your project files named ViewController1_iPhone.xib and ViewController1_iPad.xib, which do not use the tilde suffixes, and it included code to look at the user interface idiom and choose a filename accordingly. Why does the universal project template do this? I don't know, but it is stupid. I suggest you fix the filenames to use the tilde suffixes and rip out the code that checks the user interface idiom.
I would recommend moving your .xib files to Storyboards, one for iPhone and one for iPad. They put a lot of joy back into development and are easy to learn.
Then, assign your custom class to your view controllers and link your UI elements to your code. If you do this for both storyboards, then they can both share the same code by referencing a common .h/.m file.
In the project settings, you then assign the appropriate storyboards to the iPhone/iPad deployment info once the app has been configured for universal development.
It's more or less up to you how you choose to implement things and structure things, but I tend to work with the following idea :
Ignore the 'Target for iPad' and 'With Xib' options (unless not using storyboards. See later)
Create a parent view controller that holds all shared code. E.G. MainViewController
Create 2 subclasses of this for both iPad and iPhone. E.G. MainViewController_iPhone and MainViewController_iPad (you could use MainViewController~iPhone which you mentioned. Simply a matter of naming preference here)
Any code that you want shared between iPhone and iPad, stick in the MainViewController parent class, and anything specific to each device place in the appropriate subclass
Generally you shouldn't really need to test if you're running on an iPhone or iPad. That's not to say that it's wrong and you shouldn't do it, but by separating the classes like this, you shouldn't really need to. But what I like to do is check what device I'm running on when I handle what orientations the device can handle, and put this in the shared parent view controller.
As for your UI, you've got 3 options.
- Use storyboards (I'd recommend this)
- Use separate XIB files
- Code everything manually
Depending on how much you now about iOS, coding everything manually can be more efficient, but will most likely take you longer. Using interface builder is nice and simple, although any customisations you want to make you'll still need to do in code but that's fine.
I'd suggest using storyboards so that you don't have loads of different XIB files. It also simplifies the split between iPhones and iPads, as you simply have 2 files for your interface. One will have all of your screens for the iPhone, and one will have all the screens for the iPad. iOS will automatically load the right storyboard at startup so you don't have to do anything. Then, to get your view controller and view, you can do something like :
MainViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainViewControllerIdentifier"];
Note that the identifier is specified inside the storyboard.
Hopefully this helps slightly, but if you have more questions just fire away :)
Here is what you want:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
storyboard = [UIStoryboard storyboardWithName:#"ViewController1_iphone" bundle:nil];
}
else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
storyboard = [UIStoryboard storyboardWithName:#"ViewController1_ipad" bundle:nil];
}
If you name your nib files MyNib.xib and MyNib~ipad.xib, the required nib will be automatically loaded, depending on the device your app is running on. However, it is not always necessary to create different nibs. If you don't require much customization, and it can be solved with the autoresizingMasks or you are setting some frames programmatically, it can be solved with only one nib. If you check the "targeted for ipad" part, your view in the nib will be larger, and the grouped tables look a little differently, but I don't know of any other difference in the outcome.
Hope this helps!
I wanted to rename the files .h, .m. .xib into my own name.
I have created a tabbed application, and I did single click on the source browser, and changed
FirstViewController.h to FVC.h
FirstViewController.m to FVC.m
and changed respective places to include FVC.h.
It works for me. But what i would like to know is that is this the right approach that i can go ahead with?.. will there be any hidden issues with this approach?
The only "gotcha" is that if you are using the nibName in your code (ie initWithNibName:), you obviously need to change that too. I personally have gotten bit with that problem - the app launches then crashes when it tries to push that broken object.