Xcode changing constraints dependant on screen orientation - xcode

I have designed an app using auto layout and all works perfectly, issue is, when I switch to landscape I want the location of one button to move to the other side of the screen.
Im assuming the best way of doing this is by changing the constraints when I rotate the screen, is this possible in the code, or even the best way of doing it? Obviously if the app starts up in landscape I obviously want this button to be in the landscape mode also.
thanks

Absolutely. Remove and add constraints as desired in your view controller's updateViewConstraints implementation.
You will find it easiest to prepare both sets of alternate constraints beforehand, maintaining them in instance variables. Then you can just swap them in and out. Here's a sketch of what the code could look like:
-(void)updateViewConstraints {
[self.view removeConstraints:self.oneSideConstraints];
[self.view removeConstraints:self.otherSideConstraints];
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
[self.view addConstraints:self.oneSideConstraints];
else
[self.view addConstraints:self.otherSideConstraints];
[super updateViewConstraints];
}

Related

UIImageView not updating image when displayed in a UIScrollView

I am new to Xcode and objective C so the problem I have hit may be a simple one but I haven't been able to find an answer yet so thanks for any help you can give.
I am trying to write a simple app that takes a picture from the camera and displays it in a UIImageView. This all works fine if the UIImageView control is just placed in the UIView control (using the interface builder). The code I use to set the image to the control is
[self.imageViewOSFCorner setImage:image];
However I need to display several images an wanted the user to be able to scroll up and down the page. So I created a UIScrollView in the interface builder that is the size I need and placed all the controls in that. Then in the viewDidLoad method I placed the following code to display the UIScrollView
self.scrollView.contentSize = self.scrollView.frame.size;
self.scrollView.frame = self.view.frame;
[self.view addSubview:self.scrollView];
This works in that you can now scroll the page and see all of the UIImageView controls, and when I press the button it launches the camera, but now after taking the picture it doesn't display the image into the UIImageView. If I take the UIScrollView away it works again (but obviously doesn't scroll) - I am guessing I am doing something silly, is there a function I need to call to update the UIScrollView?
As an aside I originally tried to use the code
self.imageViewOSFCorner.image = image;
to display the image but this wouldn't display the image and had to use
[self.imageViewOSFCorner setImage:image];
can someone tell me why the first method didn't work?
Thanks
Will
May be useful:
self.scrollView.frame = CGRectMake(0, 0, self.view.frame.size.width*num,view.frame.size.hight );
num is your number of all image
Ok it is working now, I must have done something stupid when creating the controls into the UIScrollView since I have started from scratch again in the interface builder and now it all works.

Can I disable autolayout for a specific subview at runtime?

I have a view that needs to have its frame manipulated programmatically - it's a kind of document view that wraps to its content which is then scrolled and zoomed around a superview by manipulating the frame origin. Autolayout fights with this at runtime.
Disabling autolayout completely seems a bit harsh because it could reasonably be used to handle layout for the other views. It seems like what I might want is some kind of "null constraint".
I had the same problem. But I have resolved it.
Yes, you can disable auto layout at runtime for a specific UIView, instead of disabling it for the whole xib or storyboard which is set by default in Xcode 4.3 and later.
Set translatesAutoresizingMaskIntoConstraints to YES, before you set the frame of your subview:
self.exampleView.translatesAutoresizingMaskIntoConstraints = YES;
self.exampleView.frame = CGRectMake(20, 20, 50, 50);
I had a similar issue where Autolayout was overriding some of my frame-setting at run time (I had a dynamic view that in some cases pushed a new view controller...pushing and then pressing Back would reset the initial view).
I got around this by putting my manipulation code in viewDidLayoutSubviews of my View Controller. This seems to get called after whatever constraint mojo gets called, but before viewDidAppear, so the user is none the wiser.
Perhaps just setting translatesAutoresizingMaskIntoConstraints to YES (and not adding additional constraints affecting that view) will let you set the frame without fighting the auto layout system.
In iOS 8 you can set an NSLayoutConstraint to be active or not. So if I'm using interface builder, I add all my constraints to an OutletCollection and then activate or deactivate using:
NSLayoutConstraint.deactivateConstraints(self.landscapeConstraintsPad)
NSLayoutConstraint.activateConstraints(self.portraitConstraintsPad)
The particular application I'm using it for here is having different constraints in portrait and landscape mode and I activate/deactivate based on the rotation of the device. It means I can create some complex layout changes all in interface builder for both orientations, and still use auto layout without the verbose auto layout code.
Or you can activate / deactivate using removeConstraints and addConstraints.
I don't know if this will help anyone else, but I wrote a category to make this convenient because I find myself doing this a lot.
UIView+DisableAutolayoutTemporarily.h
#import <UIKit/UIKit.h>
#interface UIView (DisableAutolayoutTemporarily)
// the view as a parameter is a convenience so we don't have to always
// guard against strong-reference cycles
- (void)resizeWithBlock:(void (^)(UIView *view))block;
#end
UIView+DisableAutolayoutTemporarily.m
#import "UIView+DisableAutoResizeTemporarily.h"
#implementation UIView (DisableAutoResizeTemporarily)
- (void)resizeWithBlock:(void (^)(UIView * view))block
{
UIView *superview = self.superview;
[self removeFromSuperview];
[self setTranslatesAutoresizingMaskIntoConstraints:YES];
__weak UIView *weakSelf = self;
block(weakSelf);
[superview addSubview:self];
}
#end
I use it like this:
[cell.argumentLabel resizeWithBlock:^(UIView *view) {
[view setFrame:frame];
}];
Hope it helps.
You can set the translatesAutoresizingMaskIntoConstraints type Boolean, Value Yes in the User Defined Runtime Attributes of the UIView you want in the xib/storyboard.
In my view I had a Label and a Text. The label had pan gesture. The label moves around fine during drag. But when I use the text box keyboard, the label resets its position to the original location defined in auto layout. The issue got resolved when I added the following in swift for the label. I added this in viewWillAppear but it can be added pretty much anywhere you have access to the target field.
self.captionUILabel.translatesAutoresizingMaskIntoConstraints = true
Open project in 4.5
Select storyboard
Open the file inspector
Under Interface Builder Document uncheck 'Use Autolayout'
You can split across multiple storyboards if you want to use autolayout for some views.
For me it worked to create the subview programmatically, in my case the auto layout was messing with a view that I needed to rotate around its center but once I created this view programmatically it worked.
I've encountered a similar scenario, where I joined a project that was initiated with auto-layout, but I needed to make dynamic adjustments to several views. Here is what has worked for me:
Do NOT have views or components laid out in interface builder.
Add your views purely programmatically starting with alloc/init and setting their frames appropriately.
Done.
This happened to me in a project without storyboards or xib files. All 100% code. I had an ad banner at the bottom and wanted the view bounds to stop at the ad banner. The view would resize itself automatically after loading. I tried every resolution on this page but none of them worked.
I ended up just creating a sub view with the shortened height and placed that in into the main view of the controller. Then all my content went inside the sub view. That solved the problem very easily without doing anything that felt like it was going against the grain.
I am thinking if you want a view that is not the normal size that fills the window then you should use a sub view for that.
Instead of disabling autolayout, I would just calculate the new constraint with the frame you are replacing. That appears to me to be the appropriate way. If you are adjusting components that rely on constraints, adjust them accordingly.
For example, if you have a vertical constraint of 0 between two views (myView and otherView), and you have a pan gesture or something that adjusts the height of myView then you can recalculate the constraint with the adjusted values.
self.verticalConstraint.constant = newMyViewYOriginValue - (self.otherView.frame.origin.y + self.otherView.frame.size.height);
[self.myView needsUpdateConstraints];
For those of you who are using auto layout, please check out my solution here. You should be making #IBOutlet's of the constraints you want to adjust and then change their constants.
if it's xib file:
select the .xib file
select the "File's Owner"
show the Utilities
click on: "File Inspector"
Under "Interface Builder Document" disable: "Use Autolayout"

Resizing view controller acording to tabbar controller

In my application, I have developed one wizard in which I am providing a way for the user to setup his details one-by-one. After finishing all steps, the user will be redirected to the screen where TabBar will come into the picture.
The problem here is that the user can access the same view controllers with the wizard (without TabBar controllers) and normal flow (which is with tabbar controller). In the wizard, I am using a view controller of size 320x480 and the same in normal flow. But whenever I load any view controller using TabBar the 44 pixel view from bottom side gets hidden behind TabBar.
I know the I can manually set the view size, by detecting whether TabBar is present or not, but here in this case number of view controllers are more and its already designed of size 320x480.
I had tried with all methods given in Apple's documentation but none of it seems to work for me.
Following are the methods I have tried, along with some xib settings.
[self setWantsFullScreenLayout:YES];
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[self.view setAutoresizesSubviews:YES];
[self.navigationController.view setNeedsLayout];
Is there any way to set the height of a view controller according to whether that TabBar is present or not?
In my case, (Status bar/Nav bar/Hidden tabbar) this worked
//Add this in your ViewController
self.edgesForExtendedLayout = UIRectEdgeBottom;
self.extendedLayoutIncludesOpaqueBars = YES;
However Barry's answer is better if you use Storyboard but I could not for this VC.
Tried for iOs7 and iOs 8.
If you're using storyboards, select each tab VC and clear the box for View Controller > Extend Edges > Under Bottom Bars.
I wasn't able to find a good answer for this, so I ended up adding a BOOL, hasTabBar. I set it true or false based on where I create my view, and use that to calculate my layouts.
frame.size.height -= (hasTabBar*50); // works nicely.

iPad - Handling orientation change via reloaded loadView

I have a custom view which needs to be relaid on orientation change. My UIView code can correctly redraw for the any orientation as it express itself in screen frame size.
But how do I get the loadView method to run on orientation change for the right view to get drawn ?
I put [self.view setNeedsDisplay] in the following rotation handling methods with no luck.
willRotateToInterfaceOrientation:duration
didRotateFromInterfaceOrientation:
Any suggestions would be most welcome.
loadView actually causes the view to be completely re-created from scratch. It should be sufficient to call setNeedsDisplay in didRotateFromInterfaceOrientation on the view that needs to redraw.
If you have to change the layout of your views upon rotation, do so in willAnimateRotationToInterfaceOrientation (so the changes get animated), and/or set the autoresizingMask property to the combination of UIViewAutoresizingMask values that suits your needs.

NSTextField over NSOpenGLView

I have made a window with an NSOpenGLView that I am rendering openGL content into.
I want to add some buttons and text fields to the view: I can add NSTextFields and NSButtons using interface builder (or code) but they do not appear.
NSOpenGLView is documented as not being able to have sub views, so I then made my own CustomGLView by deriving directly from NSView and implementing the code to create and use a NSOpenGLContext in it. But the subviews are still not appearing :- the OpenGL context paints over them.
On Windows this problem does not exist:- Windows used to host OpenGL MUST have the WS_CLIPCHILDREN and WS_CHIPSIBLINGS styles set ensuring that any peer, or sub children (views) will not be obscured by the OpenGL surface.
How do I get subviews to display over a NSView thats drawing using OpenGL ?
You have 2 choices:
Create a window just for the text field. Add as a child window of the one hosting the OpenGL view. Major downside is you have to manage positioning it correctly if the Open GL view is moved.
Set up your view hierarchy like so:
Layer-backed view
Layer-hosting view whose layer contains an OpenGL layer
Text field
Simply call -setWantsLayer:YES on the subviews of the NSOpenGLView.
NSOpenGLView cannot have subviews according to the documentation. Even if you subclass the NSOpenGLView, that will change nothing.
What you can do is to create a NSView that will hold both the NSOpenGLView and the NSTextField. You then overlap them in the right order to make one draw atop the other.
I'm not heavily into OpenGL yet, but it's my understanding that you can accomplish the visual effect of subviews with Quartz Extreme using layer-backed views; however, those may be problematic. Since subviews are not supported directly, any solution is liable to be a hack.
Indeed, the solution in that link actually hacks a second window to appear over your OpenGL display, the second window displaying the Cocoa views you desire.
The following code (from the above link) is something I've not tested (again not being an OpenGL guy by nature -- yet), but appears like a fairly clever approach:
// This is the GL Window and view you already have
glWindow = [[GLWindow alloc] initWithContentRect:windowRect];
glView = [[[GLView alloc] initWithFrame:NSMakeRect(0, 0, windowRect.size.width, windowRect.size.height)] autorelease];
[glView translateOriginToPoint:NSMakePoint(glView.bounds.size.width/2, glView.bounds.size.height/2)];
[glWindow setContentView:glView];
// And here's your transparent UI window
uiWindow = [[TransparentWindow alloc] initWithContentRect:windowRect];
uiView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, windowRect.size.width, windowRect.size.height)] autorelease];
[uiView translateOriginToPoint:NSMakePoint(uiView.bounds.size.width/2, uiView.bounds.size.height/2)];
uiView.wantsLayer = YES;
[uiWindow setContentView:uiView];
[glWindow addChildWindow:uiWindow ordered:NSWindowAbove];
Again, I've not tested this, but it looks like it will get you the visual effect you desire.
The text can be rendered into a texture -- I just used this for a project, did a lot of looking for sample code, and ultimately found Apple's GLString demo code, which was an absolute trove of how-to:
http://developer.apple.com/library/mac/#samplecode/CocoaGL/Listings/GLString_m.html
I haven't tried adding buttons, but you can, of course, draw your own and comparing the positions of click events with those of your buttons...
This was my solution:
1) Create a parent NSView (let's call it parentView).
2) Add an NSOpenGLView Child to parentView.
3) Add an additional NSView Child to parentView (make sure this is after the OpenGLView within the hierarchy). You can add additional TextFields, etc. to this view.
4) In the ViewController for the parent make sure you call [parentView setWantsLayer: TRUE]; I did this within -(void) viewWillAppear
1) The NSOpenGLView can have a subview. It can have plenty even.
2) The reason some views, controls and other elements are being bullied by NSOpenGLView is due to the loading process when the Application launches. I.e If you add a slider or textfield above and into the content view of the window where the NSOpenGLView also resides, upon Application-Launch that textfield will most likely wind up beneath the NSOpenGLView.
This is an Apple Bug. And they know about it.
You can solve it quite easily even without adding a subview to NSOpenGLView...
In Interface Builder drag i.e. a CustomView into the canvas (Not the view). And set it the way you want it with sliders, text and what not. Then create an outlet (Call it i.e topView) in your view controller. Then somewhere in your code... Perhaps (applicationDidFinishLaunching) add this line...
[_window.contentView addSubview:_topView];
(Do your positioning & layout)
This will do the exact same thing as if you had dragged it into the contentView yourself inside IB. Only it will draw the darn thing in the correct Z position.
You loose IB's constraints this way and have to it manually
One could also just subclass and CAOpenGLLayer and use that as a backing layer inside of a regular NSView. There too it is drawn correctly...
Here is Apple's way of wanting to do that. CALayers are a Godsend ;)
Enter following String ** NSOpenGLLayer** in search and hit enter to get to where it is...
NSOpenGLLayer
Hope this helps....

Resources