iPhone : changing background image when rotating device using size classes - ios

I placed a background image that needs to fill the iPhone screen. It is fine in portait view but when I rotate the device the top is cropped which I don't want.
The best seems to be an image for the portrait view and one for the landscape view.
I tried to use the size classes, assigning 1 image for compact W and any H and 1 for any W and compact H. I can't make it work.
I am using Xcode 6.3 and swift 1.2.
I made another app using the instructions in the book iOS 8 essentials (Neil Smyth) chapter 24 but it does not work. I downloaded the file "universal_images", thinking I was doing something wrong but it does not work neither.

This is the answer to my question.
1 - I added my two background pictures (one landscape version and one portrait version) in Supporting files , in a group that I named "images" (not necessary but tidier).
2 - In Main.storyboard, I added a View (via object library on the right inside bottom) that appears inside the already present view in the view controller scene.
3 - Inside that view I put an image view and in the editor -> image view ->image selected the portrait image file. View-> mode-> Aspect to fill
4 - I add constraints to the container view and image view through the pin menu 0 bottom, top, left , right.
5 - In ViewController.swift inside the class ViewController: UIViewController {…… I added the following code:
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
if (toInterfaceOrientation.isLandscape) {
println("Landscape");
background.image = UIImage (named: "BeethovenH.png")
}
else {
println("Portrait");
background.image = UIImage (named: "BeethovenV.png")
}
}
It worked perfectly.
Thanks for your help.

I always use two images: one for portrait and one for landscape and have different resolutions for different targets. This is the best way to guarantee your background looks right on different devices with different aspect ratios.
I always make the background a UIImageView contained in a separate UIView. Add constraints to all sides to pin the container view and image view to the edges. This ensures the image fills the screen for portrait and landscape automatically.
I have the following method to set the background for a given orientation. It relies on a factory class to load the appropriate image. Loading backgrounds requires more effort than normal images as size class does not tell you anything about the aspect ratio. Replace the MyImageFactory with whatever code you use to load the appropriate image.
- (void) setBackgroundImage:(UIInterfaceOrientation) orientation{
if (orientation == UIInterfaceOrientationLandscapeLeft ||
orientation == UIInterfaceOrientationLandscapeRight)
{
NSLog(#"Setting background to landscape");
[self.backgroundImageView setImage:[MyImageFactory backgroundImageLandscape]];
}
else{
NSLog(#"Setting background to Portrait");
[self.backgroundImageView setImage:[MyImageFactory backgroundImage]];
}
}
Call the background setting method in viewWillAppear to initialise the background to the startup orientation as follows:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Set the background image
[self setBackgroundImage:self.interfaceOrientation];
}
Finally to deal with rotation, override the rotation method as follows:
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[self setBackgroundImage:toInterfaceOrientation];
// Do anything else which is rotation specific.
}
Works well on iOS7 to iOS9.

I wanted to do this and it took me forever to find an ios 11/swift 4 version of the solution so I thought I would offer it here. You follow all the steps given in the answer above but the code is:
func changeBackground() {
//iPads etc
if traitCollection.horizontalSizeClass == .regular{
background.image = UIImage(named: "beethovenV.png")
}
else {
//compact width - most iPhones in portrait
background.image = UIImage(named: "beethovenV.png")
//iphone in landscape
if traitCollection.verticalSizeClass == .compact{
background.image = UIImage(named: "beethovenH.png")
}
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
changeBackground()
}

You need to do simple 2 steps
1.) Choose size class any,any it will run on both portrait and landscape mode for all devices both for ipad and iphone.
2.) Use autolayout and set constraint as shown in screenshot
It will work fine.Thanks

Related

Autolayout: UIImageView not rotating with device orientation

There's probably a dozen SO questions with similar titles, and as far as I can see, I've incorporated the advice from every one of them, with no luck. When I rotate the device from portrait to landscape, I want to change the background image. However, in every experiment I have tried, the UIImageView in landscape remains portrait sized.
I am using Autolayout, configured in IB like so:
My view hierarchy is configured like so:
When I rotate the device, I want to rotate everything in UIView viewBackground (the image and all of the buttons). I believe I can manage the button movements through constraints, but need to rotate UIImageView image view background myself. So, I added the following code:
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
int scaleFactor = self.traitCollection.displayScale;
NSString *source = #"drawn";
NSString *orientation;
if (size.width > size.height)
{
orientation = #"Landscape";
}
else
{
orientation = #"Portrait";
}
NSString *platform;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
platform = #"iPad";
}
else
{
platform = #"iPhone";
}
NSString *filename = [NSString stringWithFormat:#"%# %# %# %.0fx%.0f.png", source, platform, orientation, size.width * scaleFactor, size.height * scaleFactor];
// CONFIRMED: we have assembled the correct new image name.
viewBackground.frame = CGRectMake(0, 0, size.width * scaleFactor, size.height * scaleFactor);
viewBackground.autoresizesSubviews = YES;
viewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.autoresizesSubviews = YES;
imageViewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.image = [UIImage imageNamed:filename];
}
The problem: The correct image appears on the screen, but as as mentioned above, it still has the portrait dimensions. I'm certain the image changes as the artwork is significantly different, with elements being stacked horizontally versus vertically. But the new picture is cut off horizontally in landscape mode.
I have tried putting all sorts of wild values into newRect, and none of them make any difference. The image never actually moves, even if I radically change the origin in newRect. so my current theory is that my changes to the view sizes are being ignored/over written.
The Question: What am I missing that's preventing the resizing of the image to landscape mode? Is there some other auto sizing aspect I'm not addressing?
So, the problem turned out to be yet another variable in the saga. There also must be a constraint on the UIImageView that is 0 on each side, with "Constrain to Margins unchecked." Apparently, when I did that step earlier, it didn't actually take.
I have to admit, though, I really don't understand what that constraint is doing for me. It clearly makes the rotation work, but I don't get why. Since that's technically the answer to my original question, I'll gladly award the answer if someone can explain how this constraint enables this scenario.
EDIT:
In case anyone stumbles upon this answer in the future... I see now that setting the aforementioned constraint with all 0 buffers between the UIImageView and the UIView pins the edges of UIImageView to the edges of the UIView. When the device rotates, the viewController resizes the UIView, and with this constraint, the UIImageView resizes also. I was able to remove the majority of my code. The only code I need is to select a portrait or landscape image, and I was able to remove everything that resized the UIView and UIImageView.
The image now changes size properly when rotating just by using the one constraint. I only need code to decide whether to show a landscape or portrait background. (And I vastly simplified that code by storing my images in an asset catalog so the code just selects the name of "portrait" or "landscape", but the size of the image is auto selected based on the device).

iOS - Need to resize a button and image for different iPhones

I have a really simple App, with one view, one button with one activity when you press this button. The button is full screen size, as well as the image that is in it.
I have it running on my iPhone 6 Plus perfectly, after spending a long time getting the Controller View, View Container, and Button sizes to work together so it is seen properly on the iPhone 6 plus, both hardware and virtual.
The button and image are full screen size, and it took some fenagling to get even this to work for ONE iPhone size. LOL. The Controller View is one size, which is NOT the real life size of my iPhone 6 plus. It is larger. So the View Container is the correct size, along with the button. The image is PNG 401PPI 1080 x 1920 as Apple recommends for the iPhone 6 plus. It looks correct on my iPhone.
My issue is the following: How do I now take my App, and create the other two sizes for the iPhone 5 and iPhone 4?
I have scoured here and on the Apple Developer website to absolutely no avail whatsoever. Nothing I have tried so far, has done anything but make me pull my hair.
I am using XCode 6.4 and targeting iOS 8 and above.
Here is what I have set within the XCode environment:
iPhone 6 plus layout details:
a. View Controller attributes:
- resize view from NIB
- Presentation : Fullscreen
- Simulated metrics iPhone 5.5 inch, fixed size (seems too big, the image is not fullsize to this size) 414 x 736
b. ViewContainer
- Mode: scale to fill (scales to View Controller above)
c. Button
- Image = iOS 401PPI.png
- Alignment = Centered horizontal and vertical
- Edge = Content
- View = Scale to Fill
— Size Inspector
View = Width 375 x Height 652
Intrinsic Size = Default
screen actual size = 667 x 375 “ViewContainer”
button size = 652 x 375 “button”
Image is original 401ppi 1080 x 1920 # 401 ppi
HELP...
iPhone and iOS system uses smart naming system to select the appropriate resolution image based on the device the image is being rendered on.
Back then, an iPhone 3 or 3gs has a screen dimension of 320x480 pixels. In this environment, if we wanted a button that is 100 x 50 pixels, then we created one of that size in Photoshop.
With the introduction of the Retina displays, the screen pixel resolution is doubled, but Apple wants to make it easier for the developer so with our Objective C code, we still tell our button to be 100x50 dimension but iOS measures it in points instead of pixels.
That way, on Retina screen, 100x50 points is converted to 200x100 pixels.
iOS will also then try to find an image with a naming suffix "#2x" and load this one if it's available.
Demo
So suppose we got this public domain eggs image here:
http://www.publicdomainpictures.net/view-image.php?image=14453&picture=easter-eggs-in-grass&large=1
Which I've resized to 600x900 for the eggs#3x.png iPhone 6/6+ image.
Then the we have:
eggs#3x.png 600x900 pixels
eggs#2x.png 400x600 pixels
eggs.png 200x300 pixels
Create a new Single View Template project in Xcode. Drag and drop all three eggs images to the Image.xcassets file manager. Then modify the ViewController file to look like this:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (nonatomic, strong) UIImageView *imageView;
#end
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self initViews];
[self initConstraints];
}
-(void)initViews
{
self.imageView = [[UIImageView alloc] init];
// ---------------------------------------------------------------
// extension not needed since we added images to the
// Images.xcassets manager instead of directly to project
// ---------------------------------------------------------------
self.imageView.image = [UIImage imageNamed:#"eggs"];
// ---------------------------------------------------------------
// image might not cover the imageView's frame, I'm using
// a grey background color so you can see where the frame is
// ---------------------------------------------------------------
self.imageView.backgroundColor = [UIColor grayColor];
// ---------------------------------------------------------------
// images may or may not be square, we use AspectFit
// to ensure we can see the entire image within the
// dimension we specified without any cropping
// ---------------------------------------------------------------
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
// add the view to the view controller's view
[self.view addSubview:self.imageView];
}
-(void)initConstraints
{
// tell iOS we want to use Autolayout for our imageView
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
// ---------------------------------------------
// name binding for our imageView for
// Autolayout Visual Formatting Language
// ---------------------------------------------
NSMutableDictionary *viewNames = [[NSMutableDictionary alloc] init];
[viewNames setValue:self.imageView forKey:#"imageView"];
// ------------------------------------------------------------------
// Autolayout code
//
// H: for horizontal constraint
// V: for vertical constraint
// |: parent view edges (so H:| == left edge for example)
//
//
// Here we're telling our imageView to be offset 50 pixels
// from the left and right as well as 50 pixels from the top
// and bottom of its parent view, which in this case, is our
// View Controller's view
// ------------------------------------------------------------------
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-50-[imageView]-50-|"
options:0
metrics:nil
views:viewNames]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[imageView]-50-|"
options:0
metrics:nil
views:viewNames]];
}
Unfortunately, Xcode no longer comes with iPhone 3/3gs simulator, only retina iPhones.
iPhone 4/4s simulator result
iPhone 5/5s simulator result
iPhone 6 simulator result
Notice how our imageView's frame remains 50 pixels offset on all four sides, relative to the view controller's view ?
If you want the image to fill the entire ImageView's frame, you can use the UIViewContentModeScaleAspectFill instead of UIViewContentModeScaleAspectFit in the above code.
This yields a result like this:
So in the case of your button image, you would do something similar.
Thank you Zhang.
I went ahead and created a new asset catalog with three images, one for each size of iPhone, and then simply dragged the images into that folder in the XCode editor window.
The contents.json asset catalog file looks like this:
Contents.json :
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "MainImage.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "MainImage#2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "MainImage#3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
With the exception that I am using Swift which makes this less code and therefore simpler, your answer is spot on. In Swift, there is no more need for two files for the ViewController, only one. I also was able to assign my sound to a single button within the ViewController, removing the need for any ImageView container for my background image:
ViewController :
import UIKit
import AVFoundation
class ViewController: UIViewController {
var myPlayer = AVAudioPlayer()
var yourSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("RemSound_01", ofType: "wav")!)
func initYourSound() {
myPlayer = AVAudioPlayer(contentsOfURL: yourSound, error: nil)
myPlayer.prepareToPlay()
}
override func viewDidLoad() {
super.viewDidLoad()
initYourSound()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playMySound(sender: AnyObject) {
myPlayer.play()
}
}
I hope this helps anyone starting out coding with Swift in XCode.

orientation in ios ipad

I have an Ipad application which support in "landscape" mode now i want this to support for both "landscape and portrait" this project is made with "xib" files
i used some delegates but i did not get solution what i have to do
i use this code
- (void)viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
//The device has already rotated, that's why this method is being called.
UIInterfaceOrientation toOrientation = [[UIDevice currentDevice] orientation];
//fixes orientation mismatch (between UIDeviceOrientation and UIInterfaceOrientation)
if (toOrientation == UIInterfaceOrientationLandscapeRight) toOrientation = UIInterfaceOrientationLandscapeLeft;
else if (toOrientation == UIInterfaceOrientationLandscapeLeft) toOrientation = UIInterfaceOrientationLandscapeRight;
UIInterfaceOrientation fromOrientation = [[UIApplication sharedApplication] statusBarOrientation];
[self willRotateToInterfaceOrientation:toOrientation duration:0.0];
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self willAnimateRotationToInterfaceOrientation:toOrientation duration:[context transitionDuration]];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self didRotateFromInterfaceOrientation:fromOrientation];
}];
}
-(void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
NSLog(#"Transition");
}
You should add UISupportedInterfaceOrientation key with list of orientations (you can make it with xCode UI in the target's General settings).
Also, if you need different orientations on different view controllers, you can override method
- (NSUInteger)supportedInterfaceOrientations
And you should read this adaptive interface, because in ios 8, orientation changing means resizing of view.
Below is an example of how to track the screen size initially and with orientation changes in Swift on iOS 8.
I would drop the trait collection delegate method, if you're just beginning. Programmatically working with constraints and transitions at that level is very involved and tricky. Some of it is evolving and/or not well documented, and things happen with constraint transitions behind the scenes that are difficult to figure out. You probably don't need to mess with things like that for awhile.
If you have to adjust constraint programmatically at all, to start, in general it is easier to do the layout in Interface builder, with constraints, and reduce the problem to making minor adjustments to the fewest number of constraints possible from the code, where you access the specific constraints via IBOutlet, and modify their .constant property (rather than remove/replace constraints, which is very difficult to get right).
If you're just getting started at this point in the iOS history, I think Swift is a better way to go than Objective C ( I've done plenty of Objective C programming) Swift is the future with Apple, and has some nice powerful features while getting around some problems and challenges in Objective C. And it offers many conveniences.
override func viewWillAppear(animated: Bool) {
adjustViewLayout(UIScreen.mainScreen().bounds.size)
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
adjustViewLayout(size)
}
func adjustViewLayout(size: CGSize) {
var width = CGSize.width
var height = CGSize.height
/* this method will be called when the view is
about to appear and whenever the orientation
changes. Adjust what you need to here based
on the incoming width and height
you can infer whether the screen is in
landscape or portrait by which dimension
is larger. Or use a switch statement
to check for specific screen sizes
and orientations. Though it may
be incomplete knowledge of layout or
a failure of design if you need to do that */
}

Can I use interface builder to my fields different positions in landscape mode and portrait mode?

Can I use interface builder to my fields different positions in landscape mode and portrait mode ? (Completely different, so I can't just use the layout properties) ?
Or is the code the only way to go ?
thanks
You can use willRotateToInterfaceOrientation method. When you change device orientation it will call..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
-(void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
{
[label setFrame:CGRectMake(20, 52, 728, 617)];
}
else
{
[label setFrame:CGRectMake(20, 52, 728, 617)];
}
}
i would say go for the code if the fields shared by portrait and landscape mode are same. In case of having different objects in each mode wont be a good idea.
You can keep two UIViews in interface builder, and when user rotate device, you can hide one and show another based on orientation. Can you please try the following lines of code?
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if(([self.navigationController.visibleViewController interfaceOrientation] == UIDeviceOrientationLandscapeLeft) || ([self.navigationController.visibleViewController interfaceOrientation] == UIDeviceOrientationLandscapeRight)){
self.viewLandscape.hidden = NO;
self.viewPortrait.hidden = YES;
}
else {
self.viewLandscape.hidden = YES;
self.viewPortrait.hidden = NO;
}
}
Here are the approaches that you can use. Best approach is on the top
1. It's better to use auto layout for adjusting your views.
2. Auto layout + code
3. Code only.
4. You can make two views for your xib one for landscape and one for portrait. And show and hide as per the orientation. But in this you need to sync all your portrait views with landscape views (properties like text) and vice versa. This is easy to maintain but you have to take extra headache for syncing the properties of each view.

ipad splash screen doesn't rotate

I would like to be able to rotate my splash screen on my ipad to landscape right and left.
i have enabled landscape right and left orientation in my .plist file. i've added 4 files for the LandscapeRight and LandscapeLeft:
Default-LandscapeRight#2x~ipad.png
Default-LandscapeLeft#2x~ipad.png
Default-LandscapeRight~ipad.png
Default-LandscapeLeft~ipad.png
although this shouldn't matter, in my rootviewcontroller i've got:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
return ( UIInterfaceOrientationIsLandscape( interfaceOrientation ) );
}
The splash screen loads, but it doesn't rotate.
what am i doing wrong?
As I Know Device does not recognise the Orientation in the duration of Splash Image.These SPlash images Default-LandscapeRight#2x~ipad.png
Default-LandscapeLeft#2x~ipad.png
Default-LandscapeRight~ipad.png recognise when app going to launch device then device takes Appropriate Splash Image.
Default-LandscapeLeft~ipad.png.
You can do the Alternate solution if you interested.and this is just my concept nothing more
1 create the UIIMageView for this Purpose and Add it as SUbview to Window.
2 Set iamge to that UIIMageView.
3 Set Sleep method for 3-4 seconds. like sleep(4).
4 As call goes to RootViewController manage the Orientation of Image.
like below method
this is the Method suppose you have defind In The AppDelegate class will manage the Image Orientation.
-(void)checkOrientationforSplash:(UIInterfaceOrientation)interfaceOrentaion
{
if (splashImageView.hidden==FALSE) {
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
if (interfaceOrentaion==UIInterfaceOrientationPortrait|| interfaceOrentaion==UIInterfaceOrientationPortraitUpsideDown) {
UIImage *image=[UIImage imageNamed:#"Default-Portrait.png"];
splashImageView.frame=CGRectMake(0.0, 0.0, image.size.width, image.size.height);
[splashImageView setImage:image];
}
else {
UIImage *image=[UIImage imageNamed:#"Default-Landscape.png"];
splashImageView.frame=CGRectMake(0.0, 20.0, image.size.width, image.size.height);
[splashImageView setImage:image];
}
}
}
5 You can Manage That Image Form in the Mid of App Instaltion, RoortViewController.
6 Remove That Splash Image At Specific Point.
-(void)removeSplash
{
[splashImageView setHidden:YES];
}
I hope it'll help you.

Resources