Part of my UIView is covering the iPad's keyboard.
The view you're seeing is simply a UIView I've added as a subview to my main view. When the keyboard is shown, I do shift the view up.
Has anyone ever seen this before?
EDIT
When the keyboard is shown, I move the view up:
[UIView animateWithDuration:SHOW_KEYBOARD_ANIMATION_DURATION animations:^{
CGRect frame = self.currentViewController.view.frame;
frame.origin.y -= SHOW_KEYBOARD_OFFSET;
self.currentViewController.view.frame = frame;
}];
EDIT
I use the follow code to add the view:
[UIView transitionFromView:fromView toView:toView duration:0.3f options:UIViewAnimationOptionTransitionCrossDissolve completion:nil];
Are you calling the code to move the view out of the way before or after the keyboard is displayed? In other words, are you registering for UIKeyboardWillShowNotification or UIKeyboardDidShowNotification?
I'm not sure where you are getting the keyboard size? Take a look at Apple's sample code again, see http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html, scroll down to the section on moving content that is located under the keyboard.
Where are you getting SHOW_KEYBOARD_OFFSET from? I bet that's the problem.
You should be catching the notifications, just as Kate said, and using the userInfo from them to work out what to do about moving your view around. Any hard coded values will break the second Apple change the keyboard size on you. In short, don't assume what the frameworks can so easily tell you.
Related
I'm trying to mimic the UITextField-keyboard relation in Whatsapp app.
As most of you know, there is a fixed UITextField at the bottom of the screen and keyboard comes up as you tap the UITextField. While keyboard is coming up, UITextField stick to the keyboard and they slide together very smoothly.
Read Apple's documentation pages which is about moving views under the keyboard several times.
What I have tried so far:
Listened to keyboard notifications. I used both setFrame: and setContentOffset:animated: method within animation block. Didn't work as good as expected. The UIViewAnimationCurve value that keyboard animation has is 7, I couldn't find what it means as UIViewAnimationCurve has only 4 integer enum type. I think animation curve of keyboard animation is not provided to developers, I hope I'm wrong.
Using custom inputAccessoryView with lower z-index value. I had two exact UIViews, one is fixed at the bottom and the other is custom inputAccessoryView that was going to hide beneath the fixed one since it has lower zPosition value. Therefore, user wasn't going to see the inputAccessoryView was coming up from outside of the screen but fixed view was being pushed up by keyboard. But no use, again. Changing zPosition value does not change anything.
At last, I thought Whatsapp might not be using system keyboard but a custom one. But if this is the case, it is a very very bad choice of design so that option is eliminated.
Anyone has an idea of how Whatsapp (or Hangouts) implemented that?
The most straightforward way of doing that turned out to be the correct way.
I should have listened to keyboardWillShow/keyboardWillHide notifications and animateWithDuration: as they are triggered. The point I missed was casting UIViewAnimationCurve into UIViewAnimationOptionCurve by shifting (<<16).
NSDictionary *notification = [aNotification userInfo];
UIViewAnimationOptions curveValue = [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue] << 16;
...
[UIView animateWithDuration:{animation duration}
delay:0
options:curveValue
animations:^{
//animation code
}
completion:nil];
The value '7' is not a power of 2, so it's not a specific animation value, rather it could be the sum (or the bitwise or) of more than one animation values.
The solution (working for me) is:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:[info[UIKeyboardAnimationDurationUserInfoKey] floatValue]];
[UIView setAnimationCurve:[userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue]];
// set view frame here
[UIView commitAnimations];
Good luck :)
UI animations are great, very easy to use, and are used allot. The only one problem I have with it is that while the animation is moving, the view in not receiving any user interaction.
For example, if a have a UIButton that animates every time it's shown, but the user will not be able to click on it until the animation is over.
//This is a UIButton:
- (void)animationApear
{
CGRect frameSelf = self.frame;
frameSelf.origin.y -= frameSelf.size.height;
[UIView animateWithDuration:1.0 delay:0
usingSpringWithDamping:0.8 initialSpringVelocity:0
options:0
animations:^{
[self setFrame:frameSelf];
} completion:nil];
}
Is there any way to deal with this issue?
Thanks!
You need to supply the option UIViewAnimationOptionAllowUserInteraction.
Also, depending on your view architecture, if the button is within the animated view, or a subview thereof, then the actual location of the button isn't moving. Only the presentation layer of the button is moving, so that is why the button may not be receiving taps. A good test is to tap where the button was when the animation started (and make sure the option UIViewAnimationOptionAllowUserInteraction is on) to see if it is still receiving taps.
On solution, when you actually need to animate buttons, is to make repeated short transforms (CGAffineTransforms, for example) and have those movements in aggregate, create the visual effect of the animation. Though in this case the button itself will move, rather than simply it's presentation.
// Animate tag view up
[UIView animateWithDuration:.2 animations:^{
_tagView.center = (CGPoint){_tagView.center.x, _tagView.center.y+40};
}];
I'm trying to animate my _tagView up from where it currently is in IB, however the above code does nothing. What am I doing wrong?
First off, if you want to move the view up, you need to decrease your y value (since origin is at upper left by default), like this:
[UIView animateWithDuration:.2 animations:^{
_tagView.center = (CGPoint){_tagView.center.x, _tagView.center.y - 40};
}];
This code works just fine for me using a button to trigger the action. What are you triggering the movement with? Here's an Xcode (iPad) project that will move a view vertically up 40 pixels every button tap.
https://github.com/perlmunger/MoveUp.git
I think you have to switch off the main view's "Use Autolayout" flag in Interface Builder. Auto Layout is enabled by default when you create a new project.
First, make sure you have added your tagView into the view hierarchy by calling addSubview:. Secondly, do your animation in viewDidAppear:; otherwise, use animateWithDuration:delay:options:animations:completion: if you are going to do it in your viewDidLoad.
I've built a UIControl subclass to display a 1-month calendar view on
an iPhone. Most months require 5 weeks to display the dates, but
some months need 6, so I've built the control to dynamically resize
itself as needed. I'm using UIView animation to change the frame of
the control here.
The problem is, I now need the other controls on the screen to
move/resize when the calendar changes size. And I really need that
to happen with the animation of the calendar control changing size.
Ideally, I'd do this without coding a bunch of details in my calendar
control about other controls on the screen.
What's the best strategy here? I was hoping I could somehow anchor
the other controls to the frame of the calendar control and have the
platform adjust their location/size as it animates the frame change.
But, thus far, I can't find any combination of struts and springs to
make that happen.
Do I just need to bite the bullet and add my other on-screen controls
to the animation happening inside my calendar control?
I'll be interested to see if there are better answers to this.
At this point, all I know to do is override setFrame on your calendar view and when it changes, send setNeedsLayout to its superview.
But I'm not sure if standard views will autoresize this correctly. Generally geometry flows down the view tree, not up, and you want it to do both.
You may have to implement layoutSubviews on the containing view.
Move the animation logic out of the specific view and into a view controller that manages all of the controls. The view can still figure out its own proper size, and let the view controller ask. For instance:
[self.calendarView setDate:date];
CGSize calendarSize = [self.calendarView sizeForDate:date];
[UIView animateWithDuration:...
animations:^{
... re-layout everything, including the calendarView ...
}];
Another, similar approach:
[self.calendarView setDate:date];
[UIView animateWithDuration:...
animations:^{
[self.calendarView sizeToFit];
... re-layout everything else ...
}];
There are lots of similar approaches based on your preferences. But the key is to move the logic out of the specific view. Usually a view controller is the best solution. If the controls make a logical collection that could go into a single UIView, then you could have that "collection" UIView manage the same thing in its layoutSubviews, but it's more common that a view controller is a better solution here.
Thanks for the help. I tried both of these approaches with success. The override of setFrame and implementation of layoutSubviews worked, but the other controls jumped to their new locations rather than animating to those locations as the calendar control grew.
The approach of moving the other controls during the animation is what I had to go with. However, I wanted to keep the logic of the control pretty self-contained for re-use, so I left the animation in the control but added a delegate to react to size change, and put the delegate call inside the animation block. In full disclosure, I am also animating the new month's calendar onto the control while I'm growing the control, and this way I could do both of those things in the animation block.
So, the end code looks something like this:
// inside the calendar control:
[UIView animateWithDuration:0.5 animations:^{
self.frame = newOverallFrame; // animate the growth of the control
_offScreenCalendar.frame = newOnScreenFrame; // animate new month on screen
_onScreenCalendar.frame = newOffScreenFrame; // animate old month off screen
if (self.delegate)
{
[self.delegate handleCalendarControlSizeChange:newOverallFrame]; // animate other controls per the delegate
}
}];
Ok, so I really like the pagecurl effect, only one problem, when sending feedback email from within the app, the partialPageCurl covers the cancel button and most of the send button for the mail. The buttons still work, but the users won't see them. Is there a way to get the partialPageCurl to a fullPageCurl where it's almost completely off screen? Thanks in advance! Here is currently how I'm pushing the view.
- (IBAction)HelpClicked{
MoreHelp *More = [[MoreHelp alloc] initWithNibName:nil bundle:nil];
More.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:More animated:YES];
[More release];
}
Check out the UICatalog code at apple's developer library. Here is the link:
http://developer.apple.com/library/ios/#samplecode/UICatalog/Introduction/Intro.html
When you run it you, select Transitions from the UITableView. See the action of 'Curl Image" button. If thats what you need...
You will find curlAction: method in TransitionViewController class. Basically it has two imageview inside a view. Based on which one is displayed and what was the previous transition (curl up or curl down), it displays the other image in a manner that it looks like you curl a page up and then curl it down. The curl imageview completely disappears. Im sure you can adapt it to your UI.
Its pretty much the same as PeyloW's answer only it uses the old setAnimation: commitAnimation pair.
No there is no full page curl variant for UIModalTransitionStyle.
If this is what you need then you must implement it manually yourself. It is more complex that doing a simple modal view controller but doable.
This code snipped only works in default portrait layout, and will need tweaking for your needs. But I hope it gives you an idea of what you need to do:
UIView* view = viewController.view;
view.frame = self.view.window.bounds;
[UIView transitionWithView:self.view.window
duration:0.2
options:UIViewAnimationOptionTransitionCurlUp
animations:^(void) {
[self.view.window addSubview:view];
}
completion:^(BOOL finished) {
[viewController.view removeFromSuperview];
[viewController presentModalViewController:viewController
animated:NO];
}];
PeyloW's and xs2bush's excellent suggestions will certainly give you a full page curl. I suspect that you're after a more-than-partial-and-less-than-full page curl though. I don't think there's an API for this, but how about a trick that might just work?
Use either PeyloW's or xs2bush's method, but also launch a timer with a duration slightly less than the UIViewAnimationOptionTransitionCurlUp animation duration.
When the timer expires, freeze the animation and remove it from the UIView that is curling up:
curledView.frame = [[curledView.layer presentationLayer] frame];
[curledView.layer removeAllAnimations];
Hopefully, this will get you the desired effect.
I'd use PeyloW's answer, but there is another way. Add a UIView to the modal view you are going to present (in viewDidLoad), and position it in the top left hand corner, and give it a backgroundColor of [UIColor clearColor]. That way the view will not be seen on screen, but the page curl transition will be forced to peel off the screen to 'show' the top-left hand corner view.