I have a series of UIButtons which I use to update a couple of UILabels and initiate animation of an object from offscreen to onscreen. The progression goes like this. Button 1 & 2 are visible and when the user pushes a button a UILabel updates and a new UIImageView animates onto the screen. This works fine the first iteration, on round two, buttons 3 and 4 which were hidden, become visible. Once the user selects either button 3 or 4, another UIImageView should animate onto the screen (on top of the other images... "stacking" them). What is happening is the UIImageView which animated on after the user pressed either button 1 or 2, disappears, and nothing animates. This only occurs if the UILabels have to update based on the button selection. In the UILabel doesnt need to update, the animation works fine.
Relevent Code:
-(IBAction)didTapSizeButton:(id) sender {
if ([sender tag] == 1) {
sizeLabel.text = #"12 oz";
}
else if ([sender tag] == 2) {
sizeLabel.text = #"24 oz";
}
if (coasterTwo.center.x != 150) {
[UIView animateWithDuration:0.35
delay:0
options:(UIViewAnimationOptionCurveEaseOut)
animations:^{
coasterTwo.center = CGPointMake(150, 220);
} completion:^(BOOL finished) {
}];
}
containerOne.hidden = NO;
containerTwo.hidden = NO;
}
-(IBAction)didTapContainerButton:(id)sender {
if ([sender tag] == 3) {
containerLabel.text = #"Bottle";
}
else if ([sender tag] == 4) {
containerLabel.text = #"Can";
}
if (coasterTwo.center.x == 150 && coasterThree.center.x != 150) {
[UIView animateWithDuration:0.35
delay:0
options:(UIViewAnimationOptionCurveEaseOut)
animations:^{
CGPoint center = coasterThree.center;
center.x += 305;
coasterThree.center = center;
}
completion:^(BOOL finished){
}];
}
}
The UIImageViews are obviously reset to their starting positions as soon as I reach back to update the UILabel... Im just not sure how to prevent that.
I would recommend using the static UIView commitAnimations method because you don't need the completion block:
[UIView beginAnimations:nil context:nil];
//Code for changing frames / alpha / anything else that is animatable
[UIView commitAnimations];
Try this out, see if it helps.
There's got to be something simple. As I said, the code for didTapContainerButton seems suspect (everytime you hit it coasterThree will be shifted another 305 points to the right), but there's nothing shown above that would cause coasterTwo to disappear. The only thing that would do that is if (a) some IB link is messed up (though it's not clear how that could result in images from disappearing when you hit button 3 or 4); (b) there's code you're not showing which might hide it; or (c) you've got some other control on your window that is hiding it (I've definitely seen situations where updating a label caused it to be brought to the front, obscuring something behind it ... you can diagnose that by making the label have a transparent background, explicitly move the image to the foreground, or try setting alphas to 0.5 so you can see through stuff while you debug).
To diagnose the first possibility, you'd have to upload your project to DropBox or something and we could take a look at it (if you're confortable doing that ... if not, make a copy of your project, get rid of anything confidential/proprietary, and then upload that).
To diagnose the second possibility, you could just update your question with the full code of the view controller's .m file.
To diagnose this third possibility, you could either make sure that before your animation of an image that you move it up front (before you animate coasterThree, do [self.view bringSubviewToFront:coasterThree];). You could also make sure that the backgroundColor of all of your controls (and if you have any container UIViews that you've added in IB, them, too) are transparent [UIColor clearColor]. You could also, just for diagnostic purposes, temporarily reduce the alpha so you can see through them (and thus if anything is blocking something else (you can do this in IB, click on the main UIView background, press command+A to select all of the controls, and change alpha to 0.5).
Bottom line, I'd pursue option 3, and if you don't have luck, either share your project or share a comprehensive code sample. If you don't feel comfortable doing that, I might suggest that you throw away your old NIB and rebuild it from scratch (in case there's something weird in the one you've got) or start building a whole new test project where you incrementally re-implement your UI and see if you can figure out where it went wrong.
But there's nothing inconsistent with UIImageViews animating and setting UILabel text value. Down below I have code with two buttons, two images that animate on and off, and a label that's updated, and it all works fine. (I had a crisis of confidence: I was starting to fear that there was some weird interaction with UILabels and animation.) Sigh.
- (void)viewDidLoad
{
[super viewDidLoad];
CGPoint center;
self.label.text = #"";
// move image 1 (dog image) off screen to the left
center = self.image1.center;
center.x = -0.5 * self.view.frame.size.width;
self.image1.center = center;
self.image1.hidden = YES;
// move image 2 (cat image) off screen to the right
center = self.image2.center;
center.x = 1.5 * self.view.frame.size.width;
self.image2.center = center;
self.image2.hidden = YES;
// Do any additional setup after loading the view.
}
// if you hit button 1 (dog) button, change label, and animate the dog image on and cat image off
- (IBAction)button1:(id)sender
{
self.label.text = #"Dog";
self.image1.hidden = NO;
[UIView animateWithDuration:0.5
animations:^{
CGPoint center;
// move image 1 (dog image) onscreen
center = self.image1.center;
center.x = 0.5 * self.view.frame.size.width;
self.image1.center = center;
// move image 2 (cat image) off screen to the right (if it's not there already)
center = self.image2.center;
center.x = 1.5 * self.view.frame.size.width;
self.image2.center = center;
}];
}
// if you hit button 2 (cat) button, change label, and animate the dog image off and cat image on
- (IBAction)button2:(id)sender
{
self.label.text = #"Cat";
self.image2.hidden = NO;
[UIView animateWithDuration:0.5
animations:^{
CGPoint center;
// move image 1 (dog image) off screen to the left
center = self.image1.center;
center.x = -0.5 * self.view.frame.size.width;
self.image1.center = center;
// move image 2 (cat image) on screen
center = self.image2.center;
center.x = 0.5 * self.view.frame.size.width;
self.image2.center = center;
}];
}
Same thing just happened to one of my students. To prove you're not going crazy... try running it in iOS 5.x simulator ( and hopefully observe the desired behavior).
For iOS 6, you'll either have to code some state preservation for your UIView -- or -- if you haven't grown to love 'em and think you can't live without 'em -- get rid of the constraints in the xib file by unchecking "Uses Autolayout" in the file inspector.
Hope this helps!
Related
I need an sliding menu mentioned in the pic which is compatible for iOS 6 and iOS 7
You can check this tutorial. It will show you how to do this on both side of the phone.
If you don't want to read all of this :
The main idea is to have a view below the main view. When you slide or touch your menu button, you just move your top view by doing the following (i did this on a class inherited by all of my view controllers, but you're free to do it anywhere you want / need it) :
// self.isMenuOpened is a flag to save menu state and set the move distance (and a few other things)
// Init move distance
float move = 270; // 270 is arbitrary value, you can set it as you like
if (self.isMenuOpened) { move = -move; }
// Moves all view except menu
[UIView beginAnimations:#"openMenu" context:nil];
[UIView setAnimationDuration:kAnimationDurationShort];
// Navigation bar frame moving (if you have one)
CGRect navFrame = self.navigationController.navigationBar.frame;
self.navigationController.navigationBar.frame = CGRectMake(navFrame.origin.x + move,
navFrame.origin.y,
navFrame.size.width,
navFrame.size.height);
// Call switching move on each view
BOOL first = true;
for (UIView *view in [self.view subviews]) {
if (first) { first = false; } // First view is menu if it's on minimum zIndex
else {
CGRect mainViewFrame = view.frame;
[view setUserInteractionEnabled:self.isMenuOpened]; // Disable your main view as it still visible
view.frame = CGRectMake(mainViewFrame.origin.x + move, mainViewFrame.origin.y,
mainViewFrame.size.width, mainViewFrame.size.height);
}
}
[UIView commitAnimations];
I iterate through all of my subviews because, in a few cases, i added a view or two. But if you have a single view (and then your menu below), you don't have to do all the iteration stuff. Hope that helps !
You can use this awesome and simple library
https://github.com/arturdev/AMSlideMenu
which supports left and right menus and fully customizable
I have two UIViews (My bad it is a UIView and a UIButton) which I am animating at the same time. I originally had a view and a containerView which would animate just fine and worked like a charm.
Now only one of my UIViews will move/animate in animateWithDuration even though through debugging the frame of the other view says that it is in a position it is not.
CGRect rect = self.toggle.frame;
CGRect tabRect = self.tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:0.3 animations:^{ // animate the following:
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
NSLog(#"%f AfterAnimation", tabButton.frame.origin.x);
The toggle view moves fine, but the tabButton view does not animate or move. The strange thing is that both the "After" and "AfterAnimation" debugging code returns the same value, which suggests the frame has indeed moved. Is there a specific reason that this will not work when toggle is a UIView when it would work as a UIContainerView?
Note that if I remove the line
self.toggle.frame = rect;
tabButton will animate correctly, but if I move toggle, tabButton will not move regardless of whether it is first in the animation block or second.
Edit: I have tried moving them into separate blocks and to change the center point rather than the frame, to no avail. It seems that if the toggle view moves, the tabButton will not move.
Edit 2: The pictorial evidence.{
In the following screenshots tabButton bg is green and toggle bg is red.
Above: Initial position (toggle is off-screen) correct position
Above: The problem in question toggle is correct tabButton is not
Above: When self.toggle.frame = rect is commented out (tabButton correct, toggle not)
}
Edit 3: It's even worse than I feared.{
I have done a few more tests and even if I take the toggle change out of the animation block to make it an instant thing, the tabButton will still not animate. This makes me think the tabButton may just fundamentally dislike the toggle view and/or myself so will not move just to spite me.
}
Edit 4:{
If I change the tabButton animation to tabButton.frame = CGRectMake(10,10,100,100) the View snaps instantly to that location and animates back to its original position in the same time as the animation duration.
}
I better add more bookkeeping/TLDR information in case things aren't clear.
toggle is an instance of ToggleDraw which is a subview of UIView which I created.
tabButton is a UIButton which is part of my IB viewController and a property of the class
Both toggle and tabButton are subviews of self.view
The animations will work individually with no modifications to the logic of the rects but will not work if they are animated at the same time
toggle animation seems to take precedence over tabButton animation regardless of the order
I had a problem with the animation of an UIView created in IB (the animation didn't start from the current position of the view, and ended in the initial position).
All worked fine after sending layoutIfNeeded() to the underlaying view before the animation block:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5) { () -> Void in
...
I think it is not a problem about a UIView Animation. Maybe your toggle posiztion is related to your tabButton. For a try, your can set toggle frame to a rect lick (10, 10, 100,100), then check the result.
I've created an example of what you describe and everything seems to work fine. This is what I used:
UIView *toggle = [[UIView alloc] initWithFrame:CGRectMake(320, 64, 100, 100)];
[toggle setBackgroundColor:[UIColor redColor]];
[self.view addSubview:toggle];
UIButton *tabButton = [[UIButton alloc] initWithFrame:CGRectMake(220, 64, 100, 100)];
[tabButton setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:tabButton];
CGRect rect = toggle.frame;
CGRect tabRect = tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:1 animations:^{ // animate the following:
toggle.frame = rect; // move to new location
tabButton.frame = tabRect;
}];
What I can suggest is to make sure that the code is being ran on mainthread:
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
});
Also take into account that the log you have after the animation code is incorrect as it won't run after the animation, but rather right next to asking for the animation.
If you want code to run after the animation you should use:
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
} completion:^(BOOL finished){
NSLog(#"Finished animating!");
}];
I have found a solution to the problem. If I initialise the UIButton (tabButton) programmatically rather than through the interface builder, both views will animate simultaneously.
This however seems very hacky to me, kind of like putting a bandaid over a missing foot and hoping it will sort itself out.
I could not work out the root cause of the problem but at least I could avoid the symptoms.
If anyone knows why the views would not animate when the button was made in the interface builder post an answer here, I am interested in knowing the reason behind this.
Thanks for your help everyone.
I wasn't quiet sure how to word this, but here goes.
I have a couple of UIControls that I am sliding into the view with in the viewWillAppear method. The UIControls animate correctly as well as my graphics inside those UIControls, but the UITextField is not. It should be starting to the right 50 points with an alpha of 0 fading into 1. Instead the UITextFields does this odd masking and from the other direction (left to right instead of right to left, as well as a bit higher on the y axis). Does this have something to do with the UITextField not being ready for display when I start my animation? Below is the code.
nameOfBillViewConstraint.constant = 50;
nameOfBillView.alpha = 0.0;
[UIView animateWithDuration:10
delay: 0.5
options: UIViewAnimationOptionCurveEaseIn
animations:^{
nameOfBillViewConstraint.constant = 10;
nameOfBillView.alpha = 1.0;
[self.view layoutIfNeeded];
}
completion:nil];
Thanks for your help on this.It's more of a visual problem, but I wasn't sure how to display that here...
I would prefer first download the project from below link and then continue with question (only 36kb)
Download Link
At start what I have is like below.
When I click My Office button, I am calling action actionSeenButton which will print NSLog(#"actionSeenButton");
- (IBAction)actionSeenButton:(id)sender {
NSLog(#"actionSeenButton");
}
This works perfect.
When I click, Show hidden button, I am sliding view by 100 and showing the image and buttons that I have at the top, as shown in below image
Code used is
- (IBAction)showHiddenButton:(id)sender {
CGAffineTransform translation = CGAffineTransformIdentity;
translation = CGAffineTransformMakeTranslation(0, 100);
[UIView beginAnimations:nil context:nil];
self.view.transform = translation;
[UIView commitAnimations];
}
When I click this button, I am calling action actionHiddenButton which will print NSLog(#"actionHiddenButton");
- (IBAction)actionHiddenButton:(id)sender {
NSLog(#"actionHiddenButton");
}
BUT the problem is, when I click the new button that I see, action is not getting called.
Any idea why this is happening?
Note
When I move the top hidden button from y=-70 to y=170, action is getting called.
Sample project can be downloaded from here
What I wanted to implement is, showing three buttons (as menu) on the top in one line by moving view down.
verify that your button is not behind the frame of another view. even if the button is visable, if there is something covering it up it wont work. i don't have access to xcode at the moment but my guess is your view "stack" is prohibiting you from interacting with the button. a button is esentually a uiview and you can do all the same animations to buttons and labels that you can with views. your best bet is to leave the view in the background alone and just move your buttons. since your "hidden" button isn't part of your main "view" hiarchy thats where your problem is.
upon further investigation, your problem is related to auto-layout and making sure your button object stays in the view hierarchy. if you turn off auto-layout you will see where the problem is. when you animate the main view down the "hidden" button is off of the view and there for inactive. the easiest solution is to just animate the buttons. the next best solution closest to what you have is to add another view onto your "main view" and then put the buttons into that view. also why do you have that background image twice? why not just set the background color of your view to that same yellow?
I downloaded your project and it seems the translation you're making for self.view. So the actionHiddenButton is not in the frame.Its better to have the controls you want to animate in the separate view.
If you want to see the problem, after your view get transformed set clipsToBounds to YES. Like
self.view.transform = translation;
self.view.clipsToBounds = YES;
Yipeee!!! Below is how I did.
.h
Added new variable.
#property (retain, nonatomic) NSString *hideStatus;
.m
-(void) viewDidAppear:(BOOL)animated {
NSLog(#"viewDidAppear");
CGAffineTransform translation = CGAffineTransformIdentity;
translation = CGAffineTransformMakeTranslation(0, -100);
self.view.transform = translation;
self.view.clipsToBounds = YES;
[UIView commitAnimations];
self.view.frame = CGRectMake(0,-80,320,560);
hideStatus = #"hidden";
}
- (IBAction)showHiddenButton:(id)sender {
NSLog(#"hideStatus===%#", hideStatus);
CGAffineTransform translation = CGAffineTransformIdentity;
if ([hideStatus isEqualToString:#"hidden"]) {
translation = CGAffineTransformMakeTranslation(0, 0);
hideStatus = #"shown";
} else {
translation = CGAffineTransformMakeTranslation(0, -100);
hideStatus = #"hidden";
}
[UIView beginAnimations:nil context:nil];
self.view.transform = translation;
self.view.clipsToBounds = YES;
[UIView commitAnimations];
}
Attached is the sample project. You can download from here.
I have a UITableView cell that has a custom label inside to handle the variable height. There is also an UIImage on the right and left.
When the table is toggled into edit mode, I want to inform and change each cell in the table, to format properly for being shifted to the right. And, when the user presses the small -, I want to further optimize to make room for the delete button.
Looking for a pattern that works for the above, assuming there is custom content in the cell that I have control over.
Thanks in advance!
Normaly all you need to do is set the springs and struts correctly and your content will slide correctly. If you create your sub-views in code then you need to make sure you call addSubview on the cell.contentView and not the cell.
To hide and / or resize the sub-views you need to override willTransitionToState:
- (void)willTransitionToState:(UITableViewCellStateMask)state
{
UIView *imageView = self.rightImageView;
UIView *labelView = self.centerTextLabel;
CGRect labelFrame = labelView.frame;
if (state & UITableViewCellStateShowingDeleteConfirmationMask) {
labelFrame.size.width += 52;
// Animating the fade while the image is sliding to the left
// is more jarring then just making it go away immediately
imageView.alpha = 0.0;
[UIView animateWithDuration:0.3 animations:^{
labelView.frame = labelFrame;
}];
} else if (!self.rightImageView.alpha) {
labelFrame.size.width -= 52;
[UIView animateWithDuration:0.3 animations:^{
imageView.alpha = 1.0;
labelView.frame = labelFrame;
}];
}
[super willTransitionToState:state];
}
I created a quick sample app up on GitHub that demos an iOS 4.3 app using a nib or code just uncomment //#define USE_NIB_TABLEVIEWCELL to use the nib
https://github.com/GayleDDS/TestTableCell.git
I personally prefer to create the table view cell in a nib file and only after profiling the App, replace the nib with code.