Swap constraints of one UIView to another UIView - ios

I have an app with certain number of UIViews. I now need to swap their positions horizontally. For example, there is a UIView called X at the left end and one called Y at the right end. At a button click, I want Y to reside where X resided and X to come to UIView Y's initial position. I figure, I must be able to do this by swapping the NSLayoutConstraints of them both(just a guess). But I can't get this to work. Is this idea possible? What will be the objective-C code for this?
These are the results I got after trying out the first code(almost there, but not completely). I have only applied it to the views KWI and DXB. What I want is a blind swap between KWI and DXB

If the views are of the same size, then it is not much of a problem. Assume your two views X and Y are called xView and yView. You could do something like this:
CGRect xViewFrame = xView.frame;
UIViewAutoresizing xViewResizingConstraints = xView.autoresizingMask;
xView.frame = yView.frame;
xView.autoresizingMask = yView.autoresizingMask;
yView.frame = xViewFrame;
yView.autoresizingMask = xViewResizingConstraints;
If you want this animated, you can put it inside a [UIView animateWithDuration: animations:^{}] block, and it should work. (I haven't tested it out though)
You could also change the CGRect xViewFrame to use CGPoint xViewCenter and swap the centers for same-sized views.
The property autoresizingMask tells the program what to do in the case of a screen resize - whether by a change in orientation, or a result of device screen size or any other reason.
If the views are NOT of the same size, you would have to deal with the proper margins of your views, and ensure that they do not get clipped outside the screen.
Hope this helps.
EDIT
It seems that you are using UILabels either as your view, or a subview of the two views. If this is the case, make sure you also swap the NSTextAlignment of the UILabels. Something like this:
NSTextAlignment xTextAlignment = xView.titleLabel.textAlignment;
xView.titleLabel.textAlignment = yView.titleLabel.textAlignment;
yView.titleLabel.textAlignment = xTextAlignment;

Related

iOS for Android View.GONE setVisibility mode

I have used storyboard with autolayout for my UI design. Basically in android there are three different properties will be there like Visible and Invisible and gone.
For Example:
1) Android
mTextView = (TextView) findViewById(R.id.textview);
mTextView.setVisibility(View.GONE);
2) Objective C
UIView *a = [self.view viewWithTag:2]
I want to process View.GONE...
for act as a gone in IOS i have searched from google but i can't able to find the Solution.
Shin,
Android does not make use of autolayouts to calculate the frame of view components and like CSS (Cascading style sheets) it uses relative values of view components with respect to parent, margin or other peer views to calculate the position of view.
But in iOS when u use auto layouts, you define the constraints which are mathematical expressions and iOS evaluates them to find the actual values for the frames of various components :)
Whats the Significance of it ?
Because auto layout constraints are maths expressions, you will have to provide iOS enough data to calculate the unknown values.
Lets consider,
As you can see the view with pink colour has a fixed horizontal spacing from the green view, so iOS calculates the x position of pink view from the maxX position of green view + horizontal spacing between green and pink view :)
Now assume you call view.GONE on green view and completely remove green view, now iOS will be baffled as it does not know what will be the x position of pink view, now it does not have enough data to calculate the x position of pink view :)
Does that mean I can never achieve View.GONE in iOS ?
You can!! only virtually though :P
HOW??
Two ways :
way 1
You can't remove a view because that will cause it to leave all other view depending on it for their frame calculation un handled. Hence consider setting width and height constraint's constants value of views to "0"
Add height and width constraints to the view which you want to hide and create a IBOutlet to height and width constraints.
Now when you want to hide simply say,
self.greenViewHeightConstraint.constant = 0
self.greenViewWidthConstraint.constant = 0
self.view.layoutIfNeeded()
Yeah!!! Now you have a view whose width and height 0, I mean view exists but does not exist!!
Way 2
But then, You dint remove it really using way 1 did you ??? Nope. You can actually remove the view using Way 2
What I want you to see here that, now I know that if I remove View1, view 2 could not calculate its x position, So I created a Leading constraint from View2 to Parent left margin and set its priority to High and set its constant to 0 :)
Now this constraint will not come into play, as long as required (1000) constraints are good enough to calculate the values, now assume you remove View 1, View 2's frame can't be calculated with Required constraints, hence high constraint kicks in and it says your pink view should be 0 pixel from parents left margin :)
Yeah :) You achieved what you wanted, now you actually removed green view and pink view still not lost its frame :)
How on earth did you remove green view ??
Wasn't that the question ?? Sorry, I got carried away :)
You can say self.greenView.removeFromSuperview()
On iOS, visible and gone are done with hidden = NO and hidden = YES
If you want it to be invisible, set the view alpha to 0.
edit: Making a view hidden doesn't make the layout hierarchy ignore it, unless it is in a stack view.
After some googling, I found this:
View.GONE - This view is invisible, and it doesn't take any space for layout purposes.
View.INVISIBLE - This view is invisible, but it still takes up space for layout purposes.
And its equivalent in ios is :
View.GONE - Uninstalling view. This can be done from storyboard as shown in the below image. You will have to uncheck the checkbox of Installed
View.INVISIBLE - View.hidden = true;

Auto layout show button round without fixing height and width

I am very new to iOS development and have never done auto layout before ,I have actually seven buttons on a view controller that needs to look round on every screen without fixing height and width....I have looked many tutorials but couldn't understand that how I can add constraints on those round buttons and show them at same position on every screen. I want the buttons to actually increase size when screen increase and decrease when screen size decreases.Please help and show which constraints should be added.!this shows how buttons are added on my view controller
If you want perfect round buttons(circle) then width and height should be same. For that set the aspect constraint with multipler 1:1 so that width and height will become equal.
Based on the the screenshot you have provided, see below how the constraints should look like:
Well, two points:
Position: Well, you need understand accurately ‘same position in every screen’, I guess you know view.frame = CGRectMake(10, 20, 50, 50)but same code not lead to 'same position' in different screen, important thing is which way you want. Think about a increasing screen, you have a square on it, what do you want this square change? Different change style lead to different code.
Size: You said you want square increasing or decreasing with screen, the basic way is let square.width && square.height changing with screen, if use frame layout you may write view.frame = CGRectMake(10, 20, SCWidth * 0.0666, SCHeight * 0.0833), certainly autoLayout support scale calculate, I recommend you use Masonry to add layout, sample code like:
[square mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.mas_width).multipliedBy(0.0083);
}];
of course if you use xib to do it, you can see constraints have multiplier property to fix problem.
Use this code to make round button..
You can programmatically get the current height of Button and then assign the half of height to corner radius to make it round.
[self.view layoutIfNeeded];
[self.view setNeedsLayout];
self.yourButton.layer.cornerRadius = self.yourButton.frame.size.height/2;
self.yourButton.clipsToBound = YES;

CGRect giving weird sizes

I am developing an iOS app and ran into a weird problem. I am doing a log of resizing of views and so on view did load, I have an ivar that holds the initial size of the view that I sized in the Interface Builder. The Initial CGFrame rect is: (x = 0, y = 63, width = 284, height = 705). The code I use to hold the initial value is:
filesFrame = self.fileView.frame;
and the only places i touch self.fileView.frame is:
self.fileView.frame = filesFrame;
self.fileView.frame = fullFilesFrame;
These are in two separate spots not right after each other
fullFilesFrame is defined as:
fullFilesFrame = CGRectMake(self.view.frame.origin.x,self.view.frame.origin.y+63,
self.view.frame.size.height,self.view.frame.size.width+63);
When I check my values after I resize back to the initial frame, The frame rect is now origin CGPoint (x=0, y=63)
CGSize (width=28, height=961)
how can this be?
This is the first time I'm dealing with resizing of views, and Im fairly new to iOS as well.
I was going to put this in a comment, but it's too long, so:
One trick I like in these situations is to override setFrame: in the class of the view I'm having trouble with, and set a break point in that overridden method. If you do that then you can see when the frame changes and take a look at the call stack.
It's not always totally straight forward, overriding setFrame:. The frame property can be set directly but it can also be derived from the view's bounds and center, so you might need to look for things setting the view's bounds or the view's center instead of the frame.
Also if the view has a non-identity transform, that will make the frame property behave very strangely. If you are setting non-identity transforms on your view you should just avoid the frame property altogether.

Auto Layout is preventing me from changing a view's center

One feature of my app is something that does automatic cropping of an image.
The basic idea is that someone would take a picture of a piece of paper (think: receipt), and then the image could get cropped automatically, after the borders of the paper are determined.
I'm able to determine the paper's border by using OpenCV. So, the next thing I do is to change the "center" property of each of my guides (just 2 horizontal and 2 vertical "lines" that can get dragged around manually).
Then, sometime shortly after I make all my calls to change each of the 4 guides, something else comes along and sets the "center" again. (I've overridden "setCenter" to prove this). The center seems to be reset by this: [UIView(Geometry) _applyISEngineLayoutValues].
I can't figure out why this is happening, or how to stop it, but it probably has to do with constraints. My view is a simple UIButton. When the user taps & drags on it with their finger, an action routine gets called that just changes the center. This works.
But in another case, I'm bringing up a UIImagePickerController. After they choose the picture, I determine the paper-bounds, change the "guides" centers, and then later on "_applyISEngineLayoutValues" sets them all back.
Any idea what's going on in this case? Or how I can set the center of a view, and have it actually stay?
The first rule of AutoLayout is that you can't update the frame, bounds or center of a view directly.
You must update the constraints related to the view so that the constraints update the view.
For instance, you first vertical line will have horizontal constraints something like...
1. Leading edge to superview = some value.
2. Width = some value.
This is enough (horizontally) to place this line on the screen.
Now, if you want to move this line to the right you can't just change the center you must do this...
1. Create a property in you view controller like this...
#property (nonatomic, weak) IBOutlet NSLayoutConstraint *verticalLine1LeadingConstraint;
// or if you're coding the constraint...
#property (nonatomic, strong) NSLayoutConstraint *verticalLine1LeadingConstraint;
2. Save the constraint in to that property...
// either use IB to CTRL drag the constraint to the property like any other outlet.
// or something like...
self.verticalLine1LeadingConstraint = [NSLayotuConstraint ... // this is the code adding the constraint...
[self.view addConstraint:self.verticalLine1LeadingConstraint];
Now you have a property pointing to this constraint.
Now, when you need to "update the center" of the vertical line 1...
// Calculate the distance you want the line to be from the edge of the superview and set it on to the constraint...
float distanceFromEdgeOfSuperview = // some calculated value...
self.verticalLine1LeadingConstraint.constant = distanceFromEdgeOfSuperview;
[self.view layoutIfNeeded];
This will update the position of the view and you won't get any errors.
You're using auto layout, so Fogmeister's answer is the right one, but not everyone can use auto layout - e.g. people who have to support the iPad 1 - so I'll leave this answer here.
If you need to use a view's frame but the system is adding constraints, then there is a workaround; but it's not pretty.
_applyISEngineLayoutValues sets your view's center and bounds, but doesn't touch frame. If you override setCenter: and setBounds: to do nothing, and then always use setFrame: in your own code, then _applyISEngineLayoutValues will leave you alone.
I'm not happy with this approach, but it's the only way I've found so far to stop _applyISEngineLayoutValues from pooing all over my layout logic.

Views dimensions as percentage

I need to set the position of the buttons, labels and views depending on the device resolution.
How can i set buttons and another views dimensions as percentage in the Interface builder ?
No, you can't. Maybe there's another way to accomplish what you want, so you should elaborate on what it is that you're trying to do exactly.
If you only have 2 elements, Normal "Springs and Struct" should be enough in IB.
They exactly match the UIViewAutoresizing that are set on a UIView.
If it's not enough you will have to use IBOutlet and calculate their size and position in your viewDidLoad method using the "bounds.size" of their super view.
[[UIScreen mainScreen] applicationFrame] will return you the size of the screen in point.
It will give you the frame or your application root view in Screen coordinate system. So if you have a status bar your origin will be (0, 20).
But Again you should check the size of the super view inside which you are placing your element to determine the positioning of your element.
myView.superView.bounds.size.height * 0.8;
If you really really really want to know the resolution in pixel of the screen you are on (which is a very very very bad idea) a UIView have a property call contentScaleFactor, but unless you are doing some very low level drawing you should never concern yourself with this.

Resources