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 */
}
Related
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
I am updating a 5-year-old app (originally written for iOS 3!). I have made decent inroads in using autolayout and addressing deprecation warnings. But the old technique used for presenting a different view controller when the device is rotated no longer works reliably.
- (void)orientationChanged:(NSNotification *)notification {
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (deviceOrientation == UIInterfaceOrientationLandscapeRight && !showingOtherVC) {
// switch to other VC
othervc.modalPresentationStyle = UIModalPresentationOverFullScreen;
[self presentViewController:othervc animated:YES completion:nil];
[self resignFirstResponder];
}
The other view controller does appear, but it's laid out sideways, for a portrait screen, not landscape, even though the device is in a landscape orientation.
How can I update this in a reasonably easy way (i.e., not a rewrite in Swift, not restructuring the app with storyboards — which Xcode doesn't seem to facilitate via copy/paste)? And, for the benefit of others who may happen on this question, what would be the more correct way to achieve this result (new VC on orientation change) if I were writing this from scratch?
Thank you.
This was a really stupid error, but in case someone else makes it and ends up here, this was the problem.
Instead of correctly returning the mask constant:
-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
return (UIInterfaceOrientationMaskLandscapeRight);
}
I was returning this other constant that autocomplete gave me:
-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
return (UIInterfaceOrientationLandscapeRight);
}
I've been looking at the same problem for so long I'm probably missing a simple solution here.
I created a small library to provide a custom UIView that sticks to the keyboard like the one for iMessage does (aka doesn't hide with keyboard): https://github.com/oseparovic/MessageComposerView
Basically the problem I'm experiencing is that when the user init's custom view I want a view with the following default rect initialized:
CGFloat defaultHeight = 44.0;
CGRect frame = CGRectMake(0,
[self currentScreenSize].height-defaultHeight,
[self currentScreenSize].width,
defaultHeight)
This requires that the currentScreenSize is calculated within the UIView. I've tried multiple implementations all of which have their downsides. There doesn't seems to be a good solution due to this breaking principles of MVC.
There are lots of duplicate questions on SO but most assume you have access to the rest of the code base (e.g. the app delegate) which this custom view does not so I'm looking for a self contained solution.
Here are the two leading implementations I'm using:
NextResponder
This solution seems to be fairly successful in a wide variety of scenarios. All it does is get the next responder's frame which very conveniently doesn't include the nav or status bar and can be used to position the UIView at the bottom of the screen.
The main problem is that self.nextResponder within the UIView is nil at the point of initialization, meaning it can't be used (at least not that I know) to set up the initial frame. Once the view has been initialized and added as a subview though this seems to work like a charm for various repositioning uses.
- (CGSize)currentScreenSize {
// return the screen size with respect to the orientation
return ((UIView*)self.nextResponder).frame.size;
}
ApplicationFrame
This was the solution I was using for a long time but it's far more bulky and has several problems. First of all, by using the applicationFrame you have to deal with the nav bar height as it will otherwise offset the position of your view. This means you have to determine if it is visible, get its height and subtract it from your currentSize.
Getting the nav bar unfortunately means you need to access the UINavigationController which is not nearly as simple as accessing the UIViewController. The best solution I've had so far is the below included currentNavigationBarHeight. I recently found an issue though where this will fail to get the nav bar height if a UIAlertView is present as [UIApplication sharedApplication].keyWindow.rootViewController will evaluate to _UIAlertShimPresentingViewController
- (CGSize)currentScreenSize {
// there are a few problems with this implementation. Namely nav bar height
// especially was unreliable. For example when UIAlertView height was present
// we couldn't properly determine the nav bar height. The above method appears to be
// working more consistently. If it doesn't work for you try this method below instead.
return [self currentScreenSizeInInterfaceOrientation:[self currentInterfaceOrientation]];
}
- (CGSize)currentScreenSizeInInterfaceOrientation:(UIInterfaceOrientation)orientation {
// http://stackoverflow.com/a/7905540/740474
// get the size of the application frame (screensize - status bar height)
CGSize size = [UIScreen mainScreen].applicationFrame.size;
// if the orientation at this point is landscape but it hasn't fully rotated yet use landscape size instead.
// handling differs between iOS 7 && 8 so need to check if size is properly configured or not. On
// iOS 7 height will still be greater than width in landscape without this call but on iOS 8
// it won't
if (UIInterfaceOrientationIsLandscape(orientation) && size.height > size.width) {
size = CGSizeMake(size.height, size.width);
}
// subtract the height of the navigation bar from the screen height
size.height -= [self currentNavigationBarHeight];
return size;
}
- (UIInterfaceOrientation)currentInterfaceOrientation {
// Returns the orientation of the Interface NOT the Device. The two do not happen in exact unison so
// this point is important.
return [UIApplication sharedApplication].statusBarOrientation;
}
- (CGFloat)currentNavigationBarHeight {
// TODO this will fail to get the correct height when a UIAlertView is present
id nav = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([nav isKindOfClass:[UINavigationController class]]) {
UINavigationController *navc = (UINavigationController *) nav;
if(navc.navigationBarHidden) {
return 0;
} else {
return navc.navigationBar.frame.size.height;
}
}
return 0;
}
Does anyone have suggestion about how I can best calculate the UIViewController size from within this UIView. I'm totally open to other suggestions on how to stick the UIView to the bottom of the screen upon initialization that I may have overlooked. Thank you!
+ (id) getCurrentUIViewController : (id)res {
if([res isKindOfClass:[UIViewController class]]) {
return res;
}
else if ([res isKindOfClass:[UIView class]]) {
return [Function getCurrentUIViewController:[res nextResponder]];
}
else {
return nil;
}
}
I came across an interesting problem that only arises on iPhone 6/6+ and iPad mini with retina display.
In the following code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(self.seeMoreContents)
{
BOOL isText = [self.seeMoreContents isText];
self.imageView.hidden = isText;
[self.textView removeConstraint:self.textHeightConstraint];
[self.textWrapperView removeConstraint:self.textWrapperHeightConstraint];
if (!isText)
{
__weak FCSeeMoreViewController *weakSelf = self;
[self.imageView setImageByFlashcardSide:self.seeMoreContents completion:^(BOOL preloaded){
weakSelf.imageView.center = CGPointMake(weakSelf.view.frame.size.width / 2, weakSelf.view.frame.size.height / 2);
[weakSelf.scrollView setContentOffset:CGPointMake(0, 0)];
}];
}
}
}
- (void)viewDidLayoutSubviews
{
if ([self.seeMoreContents isText])
{
self.textView.text = self.seeMoreContents.text;
self.textView.font = self.fontForContents;
self.textWrapperView.hidden = NO;
[self.textView sizeToFit];
CGFloat height = self.textView.frame.size.height;
[self updateView:self.textView withConstraint:self.textHeightConstraint ofValue:height];
[self updateView:self.textWrapperView withConstraint:self.textWrapperHeightConstraint ofValue:height + self.wrapperMargin];
[self.scrollView setContentSize:CGSizeMake(self.textView.frame.size.width, height + self.scrollTextMargin)];
[self.scrollView setContentOffset:CGPointMake(0, -self.wrapperScrollVerticalConstraint.constant)];
}
[super viewDidLayoutSubviews];
}
- (void)updateView:(UIView*)view withConstraint:(NSLayoutConstraint*)constraint ofValue:(CGFloat)value
{
constraint.constant = value;
[view addConstraint:constraint];
}
By the time the two messages of udpateView get passed, the constraints have become nil. I could attribute this to weird garbage collection behavior, but it only happens on iPhone 6/6+ and mini retina iPad.
I have changed this whole controller to work better and to not to programmatically set constraints, but I want to know how/why this can happen on specific devices. Any insight would be greatly appreciated.
Override this method in your UIViewController to detect changing of 'traits':
func willTransitionToTraitCollection(_ newCollection: UITraitCollection,
withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
This is where you intercept constraint changes to get them at the right time, otherwise it's a race and your code can lose.
I suspect not using that function to get the timing right may be why you are not seeing consistent results. I bumped into the same kind of problem awhile back - not finding constraints that should have been there when I went looking for them.
Another thing to consider about mysterious constraints appearing and disappearing, of course, is UIView's
translatesAutoresizingMaskIntoConstraints
property, which if true (the default), causes iOS to dynamically create constraints, besides whatever you may have created programmatically or created in Interface Builder. And I have noticed some iOS generated constraints can disappear in different devices and orientations, as the iOS implementation that applies and removes such constraints is a black box.
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.