Adding a subview to view changes other subviews' frames - ios

A uiviewcontroller has two UIViews and one tableview added in stroyboard ib and connected with IBoutlet
when user click swipe in top uiview, I call following method to change frames of all subview IBoutlet elements. That works.
-(void)hideTopViews
{
[UIView animateWithDuration:0.25 animations:^{
[_overallHealth setHidden:YES];
_overallHealth.frame = CGRectMake(0, 0,self.view.frame.size.width,0);
[_healthBar setHidden:YES];
_sortView.frame = CGRectMake(0, 45,self.view.frame.size.width,_sortView.frame.size.height);
_portfoList.frame = CGRectMake(0, _sortView.frame.origin.x+_sortView.bounds.size.height+50,self.view.frame.size.width,self.view.frame.size.height);
_sortButton.frame = CGRectMake(90, 45,_sortButton.frame.size.width,_sortButton.frame.size.height);
[self.view bringSubviewToFront:_sortButton];
self.navigationItem.leftBarButtonItems=nil;
}];
}
However when I add another subview programmatically to self.view all subviews goes back to their original positions like in storyboard.
-(void)showPortfolioDetailsScreen
{
PortfolioDetails *portView=[[PortfolioDetails alloc] initWithFrame:CGRectMake(300,200,200,200)];
portView.backgroundColor=[UIColor redColor];
[self.view addSubview:portView];
}
How can I fix this?

#tanzolone is on right track, if you are using only portrait mode and want your storyboard to stop changes your subviews to original frames go to your storyboard and uncheck Use Autolayout
so you can use the old struts-and-springs model.
here is a detailed tutorial of autolayouts though
http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

Related

How to Remove and Adding Constraints when we change simulator orientation(like portrait to landscape) in ios

Hi i am very new for Auto-layouts and in my project i have added Two UIViews programmatically using Auto-layouts and i have added two UIButton which are Next and Back button and when i click Next button i push MyFirst UIView to second UIView using UIView animations and when i click Back button i push back from Second UIView to First UIView ok Everything is all right based on my code
But here my main problem is when i change First UIView orientation in simulator at portrait to landscape then Second UIView overlapped on my First UIView, And i know that it is must be Constrains issue i mean i have to remove and adding Constraints each time when we change Orientation for this i have tried below code but that's not working please help me and for this i have tried since long time but no one saying right answers using constrains
when we run program at portrait mode screen is coming like image1 and when i change land scape mode second UIview overlapped on my first UIview like below second image that's my main problem
my code:-
#import "SubViewController.h"
#interface SubViewController (){
UIButton * GoNext;
UIButton * GoBack;
NSArray * FHorizental;
NSArray * FVertical;
NSArray * SHorizental;
NSArray * SVertical;
}
#end
#implementation SubViewController
#synthesize MyFisrtView,MySecondView;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"How are you");
[self callAutolayouts];
MyFisrtView.hidden = NO;
MySecondView.hidden = YES;
}
-(void)callAutolayouts{
NSLog(#"Hi");
MyFisrtView = [[UIView alloc] init];
MyFisrtView.backgroundColor = [UIColor orangeColor];
MyFisrtView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:MyFisrtView];
MySecondView = [[UIView alloc] init];
MySecondView.backgroundColor = [UIColor lightGrayColor];
MySecondView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:MySecondView];
//Applying autolayouts for MyFirstView and MySecondView
NSDictionary * HeaderDictionary = NSDictionaryOfVariableBindings(MyFisrtView,MySecondView);
//Appliying Autolayouts for FirstView
FHorizental =[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-0-[MyFisrtView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
FVertical = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-0-[MyFisrtView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
[self.view addConstraints:FHorizental];
[self.view addConstraints:FVertical];
//Appliying Autolayouts for SEcondView
SHorizental =[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-0-[MySecondView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
SVertical = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-0-[MySecondView]-0-|"]
options:0
metrics:nil
views:HeaderDictionary];
[self.view addConstraints:SHorizental];
[self.view addConstraints:SVertical];
GoNext = [UIButton buttonWithType: UIButtonTypeRoundedRect];
GoNext.frame = CGRectMake(50, 50, 100, 18);
[GoNext setTitle:#"Next" forState:UIControlStateNormal];
[GoNext addTarget:self action:#selector(GoNext:) forControlEvents:UIControlEventTouchUpInside];
GoNext.backgroundColor = [UIColor lightGrayColor];
[MyFisrtView addSubview:GoNext];
GoBack = [UIButton buttonWithType: UIButtonTypeRoundedRect];
GoBack.frame = CGRectMake(50, 50, 100, 18);
[GoBack setTitle:#"Back" forState:UIControlStateNormal];
[GoBack addTarget:self action:#selector(GoBack:) forControlEvents:UIControlEventTouchUpInside];
GoBack.backgroundColor = [UIColor orangeColor];
[MySecondView addSubview:GoBack];
}
-(void)GoNext:(id)sender{
MySecondView.hidden = NO;
MySecondView.frame=CGRectMake(248, 0, self.view.frame.size.width, self.view.frame.size.height); // starting visible position
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
[MySecondView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; // final visible position
}
completion:nil];
}
-(void)GoBack:(id)sender{
MySecondView.frame=CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); // starting visible position
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
[MySecondView setFrame:CGRectMake(MyFisrtView.frame.size.width, 0, self.view.frame.size.width, self.view.frame.size.height)]; // final visible position
}
completion:nil];
}
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
if(toInterfaceOrientation == UIInterfaceOrientationPortrait){
NSLog(#"portrait");
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
}
else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
NSLog(#"LandscapeRight");
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
}
else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
NSLog(#"LandscapeLeft");
[self.view removeConstraints:self.view.constraints];
[self callAutolayouts];
[self.view setNeedsLayout];
}
}
#end
image1:-
image2:-
First you have to remove the constraints and then add agin as you did in your question.
You could follow a tutorial on auto layout so that you can get a little know how about it. Here's a link.
Another option is the use of springs and struts.
I hope that answers your question.
Based on your comments below I'm updating my answer but leaving the original below as it may help you or others down the road.
Your issue isn't really Auto Layout then, your real issue is an architecture design issue. Every view that comes onto screen should have a different view controller running it. It sounds like you are drawing one view that is double the width of the device, one half (I'm guessing the left half) has the next button on it while the other half includes the back button. As soon as your app runs on a screen with a different size this will break unless you are checking for the screen size. Even then it breaks (as you have seen) when the device is rotated.
You need to make two different view controller subclasses, one to run the 'Next' view and one for the 'Back' view then you will never have the both views onscreen because the device got rotated.
The really hard way to get the effect you want is to do it all in code like you've tried thus far. If there is some strange reason that you just have to code it all instead of using the storyboard that was included when you started the project you will need to look at the animation methods of UIView (specifically look at the documentation for + (void)transitionFromView: toView: duration: options: completion: if you want something like a flip or page curl). If you want to slide between the two views you should really look into UINavigationController as it does all that for you. The other option would be when the Next button gets pressed create the second view controller, who will create the second view, and set it's frame to be off screen. Then animate the frame of the view to the onscreen position. Back would have to animate the frame of the Back view off the screen again and remove the view. It will take a lot of code to get all of this working which will mean reading a lot of documentation, I highly recommend using a storyboard to take care of this issue.
If you want the easy way use the storyboard and drag out a second view controller. Add a button to each scene in whatever location you want them to be in and set the text as desired. Control drag from the Next button to the second scene and select the kind of transition you would like. Add the following code to the view controller running the view with the Next button
#IBAction func exitToViewController (sender: UIStoryboardSegue){ }
Control drag from the Back button to the Exit icon (top of the scene all the way to the right, it's red with a white box and an arrow point to the right) select exitToViewController. Select one of the scenes and select Editor -> Resolve Auto Layout Issues -> Reset To Suggested Constraints (the second one under all views). It takes five minutes to do and works wonderfully.
If you need help with any of the tasks mentioned in the Storyboard route consider finding a beginner level tutorial on Xcode (there is a pretty good overview class on Coursera.org that isn't terribly long but covers many of the basics called,Foundations of Objective-C App Development and it's free if you don't care about a verified certificate.
Original Answer
Is there a reason the UI has to be built programmatically?
Building it in IB will make your life much easier as you can build the base version of the UI and then create overrides for specific size classes (look at the bottom of IB where it says w Any h Any and change the selection to be compact height). Once in the compact height make any modifications you need to the constraints and visibility of UI elements. The system should take care of things from there (you might have to manually hide and show the correct UI elements as need for the orientations).
When building in IB one of the Assistant editors is preview. You can select various devices and change the orientation between landscape and portrait, this will allow you to see in real time what your view is going to do when run for real rather than making a change in code and then running the app again. You can add one of each device if you want and they can all have their own orientation allowing you to see the UI in landscape and portrait at the same time.
If it does has to be done in code and you have to actually remove the unused elements from the UI rather than hiding them you will have to set all the constraints for the landscape orientation every time the device rotates to that orientation, the same is true for the portrait orientation. The best thing to do is to make the constraint changes in an animation block rather than manually updating the frames, this will cause the views to animate into place (you can do any alpha adjustments at the same time to fade elements in and out).

Label Text in Objective-C

I have problem about Label.Test
CGRect frame = _gobutton.frame;
frame.origin.x=rand()%330+1;
frame.origin.y=rand()%550+1;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
_gobutton.frame = frame;
[UIView commitAnimations];
i=i+1;
self.score.Text = [NSString stringWithFormat:#"%i",i];
I'm trying to move the button around the screen and when I click the button show the score in the label. Currently, when I click the button the score increases but my button moves back to its original locations.
How do I fix this?
The code you posted should animate your button to a new location. As holex says in his comment, you should really be using the newer methods like animateWithDuration:animations: and its variants.
If you are using auto-layout (the default or XIB files and storyboards in Xcode 5 and later) then you can't animate a view (or, which is a type of view) by moving it's frame. Instead you need to use position constraints, attach outlets to the constraints, and change the constraint's constant values.
If you want to animate the frame, you need to turn off auto-layout on your XIB/Storyboard and use the older struts and springs style resizing.
Since you are using the storyboard with auto-layout it will always return to its position.
If you plan on animating a view I would create it programmatically.
_gobutton = [[UIButton alloc] initWithFrame:CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight)];
[_gobutton addTarget:self action:#selector(yourButtonSelector:) forControlEvents:UIControlEventTouchUpInside];
[_gobutton setBackgroundImage:[UIImage imageNamed:#"aphoto"] forState:UIControlStateNormal];
[self.view addSubview:_gobutton];
Just take the button off of the storyboard and create your button like that. You will be able to move it anywhere you want. Its easier then changing the storyboard constraints programmatically.

Fade UIBarButtonItem When Swapped

When I change my UIBarButtonItems they change abruptly, unlike the default which gives a nice but speedy fade animation. You can see this when segueing between view controllers, for instance, the back button will fade in and out. How can I simulate the same effect?
Update - Based on this answer - https://stackoverflow.com/a/10939684/2649021
It looks like you would have to do something like this to make the button itself fade out.
[self.navigationItem setRightBarButtonItem:nil animated:YES];
And do something like this to make it fade in
[self.navigationItem setRightBarButtonItem:myButton animated:YES];
Otherwise if you want more control over animation properties you would have to create a custom view I believe.
EDIT: I just confirmed that you can fade a UIBarButtonItem custom view using this.
As a test I created a simple project and dropped a UIBarButtonItem onto the navigation bar. I created an outlet to the view controller. In viewDidLoad on the view controller I setup a custom view
-(void)viewDidLoad{
[super viewDidLoad];
UILabel *lbl = [[UILabel alloc]initWithFrame:CGRectMake(0,0,40,40)];
lbl.text = #"test";
UIView *customView = [[UIView alloc]initWithFrame:CGRectMake(0,0,40,40)];
[customView addSubview:lbl];
self.barButtonItem.customView = customView;
}
As a test in viewDidAppear I animated it
-(void)viewDidAppear:(BOOL)animated
{
[UIView animateWithDuration:3.0
delay:3.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.barButtonItem.customView.alpha = 0;
}
completion:^(BOOL finished) {
NSLog(#"animation complete");
}];
EDIT: Here's a link to Apples Documentation for a full explanation of UIView animations.
https://developer.apple.com/library/ios/documentation/windowsviews/conceptual/viewpg_iphoneos/animatingviews/animatingviews.html
You might have to create a custom bar button item using a view or an image, and then animate the properties of the view as digitalHound shows.

Animating two UIView subviews at the same time does not work

For some reason i am not able two animate two subviews postion. I have wrote the following
[self addChildViewController:self.photosViewController];
[self.photosViewController.view setFrame:CGRectMake(0, -self.view.frame.size.height, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:self.photosViewController.view];
[UIView animateWithDuration:1 delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[self.stepsView setFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, self.view.frame.size.height)];
[self.photosViewController.view setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
}
completion:^(BOOL finished) {
if (finished) {
button.tag = 0;
}
}];
self.stepsView is an IBOutlet UIView and self.photosViewController.view is an child view controllers view that has been added to the view.
With the current code only the self.PhotosViewController.view animates. However if i comment out the line where i add the child view controllers view as a subview then the self.stepsView animates correctly.
Even if i add the child view controller and its view before this method is called the same error happens.
Need help as i ran in to this a couple of months back with another app and had to do a dirty hack to get around it and now want to solve this.
Thanks
In the section on view animation in the View Programming Guide there is a specific mention of how to animate subviews.
Basically, you should use transitionWithView:duration:options:animations:completion: rather than animateWithDuration:delay:options:animations:completion:.
The first view will not be visible, because its origin.y is beyond the height of the visible view. If it was not visible when the animation starts, you will of course see nothing.
The second view is changed to fill the screen. Again, what you see depends on its initial position.

Animated Subviews

I have a split view controller in my Ipad app, in which the detail view has subviews which are UITableView's.
The functionality I am trying to implement is to get an info view at the same position of a subview when a button(info button) on the subview is pressed. I need to get this info view by animation and that animation is that when the info button is pressed, that subview alone would flip (UIAnimationOptionFlipFromRight) and the info view would show...
This is how try to implement it -
-(void) showInfoView: (id)sender
{
infoView = [[InfoViewController alloc] initWithNibName:#"ViewViewController" bundle:nil];
infoView.view.frame = CGRectMake(250, 300, 200, 200);
[UIView transitionWithView:self.view duration:1
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[self.view addSubview:infoView.view];
}
completion:nil];
}
When I run the simulator and press the info button on any subview, what happens is that the animation happens perfectly, i.e. the subview flips from the right but the infoView is not getting displayed.
It would be great if someone could tell me where I am going wrong here.
The basic steps of performing an animation to onscreen is as follows:
Create the view.
Move it to the initial (offscreen) position
Add to the view hierarchy
Perform the animation.
It seems like you're skipping step 3 and possibly step 2.
Did you try setting the subview frame to the frame of your superview, i.e
info.View.frame = self.view.frame;

Resources