Resetting UIWebView Frame kills 75% of touchable area - ios

I've got a webpage that is being viewed with a UIWebView. It's got an input form, so I'm shifting the UIWebView up when the keyboard is summoned, and then moving it back down when the keyboard is dismissed, so that the selected input field is centered. Code below:
-(void)keyboardWillShow:(NSNotification*) aNotification {
float offset = [self calculateYOffsetForKeyboard];
[UIView animateWithDuration:0.3f delay:0.05f options:UIViewAnimationOptionBeginFromCurrentState animations:^ {
self.web.frame = CGRectMake(webView_rect_x, offset, self.web.scrollView.frame.size.width, self.web.scrollView.frame.size.height);
} completion:nil];
}
-(void)keyboardWillHide: (NSNotification*) aNotification {
[UIView animateWithDuration:0.3f delay:0.05f options:UIViewAnimationOptionBeginFromCurrentState animations:^ {
self.web.frame = CGRectMake(webView_rect_x, 0, self.web.scrollView.frame.size.width, self.web.scrollView.frame.size.height);
} completion:nil];
}
Here's the weird part: when I do that (tap an input field to summon the keyboard then tap "Done" to dismiss it), everything but the top-left quarter of the UIWebView becomes untouchable. It looks fine, animates up and down perfectly, displays the content properly, but anything right of or below the center point doesn't respond to touches (i.e. several buttons can't be pressed, etc).
Any idea why this might happen? I've commented out lines and determined that it is the "self.web.frame" that is causing the problem: remove that movement and it works fine. I've also NSLog'd the "self.web.scrollView.frame.size.width" and "self.web.scrollView.frame.size.height" values and they seem to be changing as expected and returning to normal. Also the calculateYOffsetForKeyboard method is returning a proper value, (it changes based on which field is selected, but it's generally around -108.5).
So why would moving the frame back and forth alter the view's ability to respond to touches? Any ideas?

Related

Animation Blocks resets to original position after updating text

I'm currently testing my apps for the release of IOS 8. I noticed that after I performed an animation block, the animation resets if I update the text of any label. I ran a simple example with one method shown below. Running this example results in the following:
Clicking myButton the first time- animation runs but resets when the label text is changed.
Clicking myButton the second time - animation runs but does not reset to original position.
It seems like this happens because the label text doesn't change. If I completely remove the line updating the text, this also stops the animation from resetting at the end.
I would like to fix this so that when the method runs, the label text can be updated without resetting the animation.
- (IBAction)move:(id)sender {
[UIView animateWithDuration:0.4 delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.myButton.center = CGPointMake(200, 300);
}completion:^(BOOL finished){
if(finished){
self.myLabel.text=#"moved";
}
}];
}
This problem can be caused by having Auto Layout set on the UIView. Strictly speaking, if you're using Auto Layout, then you shouldn't animate the absolute position of objects -- you should animate their constraints instead.
Changing the label text once your animation is underway triggers a layout refresh, and iOS shuffles everything around to comply with the original view constraints. (I suspect this is a behavioural change from iOS7).
Quick fix: un-check Auto Layout on the View, and this should work as expected.
Try this. Put the desired animation in the finish block also.
- (IBAction)move:(id)sender {
[UIView animateWithDuration:0.4 delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.myButton.center = CGPointMake(200, 300);
}completion:^(BOOL finished){
if(finished){
self.myLabel.text=#"moved";
self.myButton.center = CGPointMake(200, 300);
}
}];
}

UIButton not responding after animation

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.

Why is uiview repositioned when application re-enters active state

I have an iOS application where the main screen that is launched (A login screen) has 3 text fields and a button to "Login" below the fields. There is a content view within the main view which holds the fields and the button and it is vertically centered. When any of the 3 fields begins editing, the view shifts up such that they are visible and so is the button. The relevant code for this is:
(void)viewDidLoad {
[super viewDidLoad];
[userName addTarget: self action: #selector(slideFrameUp:) forControlEvents: UIControlEventEditingDidBegin];
[userName addTarget: self action: #selector(slideFrameDown:) forControlEvents: UIControlEventEditingDidEnd];
//.... same thing for 2 other fields
}
-(void) slideFrameUp: (id)sender {
CGFloat slidePoints = 116.0;
[self slideFrame: YES distance: slidePoints];
}
-(void) slideFrameDown: (id)sender {
CGFloat slidePoints = 116.0;
[self slideFrame: NO distance: slidePoints];
}
-(void) slideFrame:(BOOL) up distance: (NSInteger)amount {
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -amount : amount);
[UIView beginAnimations: nil context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
This works fine and does what we need. The one hiccup is that when the user is editing a text field and presses the home button and then opens the app again, when the app re-opens (if it only went to background and wasn't killed) it will be positioned exactly how it was previously initially (for like 1/10th of a second) and then it will reset back to center with the keyboard still present, thus leaving it lower on the screen than it should be and hiding some fields. The thing is none of my code runs when the view position resets. slideFrame is not called at all either when the app enters background or when it goes back to active. None of my view related methods (viewDidLoad/viewDidAppear/viewWillAppear/viewWillDisappear, etc) are called on entering background or active. I'm wondering why does ios reposition my view when going back to active, and how can I prevent it?
I figured out one solution which I didn't really like. What I did was observe UIApplicationDidEnterBackgroundNotification and then dismiss the keyboard using the code:
[self.view endEditing: YES];
This seems to work fine except when the app opens the user is no longer focused in the field. I would prefer though if the app just opened back up as it was. I'm also just kind of curious why the system behaves this way and if maybe there is a more elegant way to handle this.
My approach would be to dismiss the keyboard as the app goes to background and set focus on your text field in viewWillAppear:

ios animation, rotating button, button suddenly changes position

I am trying to implement animations in my application. I just want to try a simple rotation of a button. The button itself is rotated, but also it changes its position and the rotation angle is different than the one I want. I have also added the autoresizesSubwievs = NO;
- (IBAction)rotateView:(id)sender
{
[UIView animateWithDuration:0.6f delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.view.autoresizesSubviews = NO;
[self.buttoner setTransform:CGAffineTransformRotate(self.buttoner.transform, 90.0f)];
} completion:nil];
}
What am I doing wrong? Can you please help me?
EDIT:
I have two buttons, when the first gets pressed, I call the above method ... but this causes to move the first button and then to move the second button and rotate it with inappropriate angle. I dont understand that at all..
For the angle problem, Instead of passing 90, you should pass the radian value of the angle which is M_PI/2 .
[self.buttoner setTransform:CGAffineTransformRotate(self.buttoner.transform, M_PI/2)];

Animate image location different after returning to page

I have a tabbed application for iPad. On the first tab I have a separate menu with a pointer that highlights the currently active menu button. The pointer (a UIImage) is animated into position after tapping the button.
The problem is when you leave the highlighted image on any button that's not the original/default button and move to another tab in the application, then back again, the image has moved back to the default location. However, when you tap another button the image moves in the wrong direction. I think it's because the image moves back but the old coordinates are retained, or the other way around? It's a bit hit and miss and behaves differently testing in the simulator compared to directly on the iPad.
I think I need to start with absolute coordinates and stick with them throughout the process. But I can't get my head around how to achieve it with the CGAffine functions.
Here is my current code
- (IBAction)diplayForm1:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 0);
[UIView commitAnimations];
}
- (IBAction)diplayForm2:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 80);
[UIView commitAnimations];
}
- (IBAction)diplayForm3:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 160);
[UIView commitAnimations];
}
EDIT - - -
#Mundi I tried the code you suggested. I'm assuming by newFrame you mean define a new UIImageView and property which I did in the .h (I called it 'sfh'), then #synthesize in the .m.
This is my code in the .m
-(void)viewWillAppear:(BOOL)animated {
// The "pointer" and the "old" frame are the same so I thought it pointless to do
// selectedFormHighlight.frame = selectedFormHighlight.frame;
// I tried anyway and also tried this and many other combinations
selectedFormHighlight.frame = sfh.frame;
}
-(void)viewDidAppear:(BOOL)animated {
[UIView beginAnimations:nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0,0,0,0);
}];
[UIView commitAnimations];
}
When I run this the pointer animates off the screen (top-left) and doesn't come back. I have tried every possible combination I can think of in viewWillAppear and viewDidAppear but it makes no difference. I tried removing the other lines in viewDidAppear and it still does the same thing.
I think you're suggesting to animate the pointer from the original (default) position when the user returns from another screen. I don't want that, it should simply remain in the same spot where they left it without animation. Or at least put it back there without them realizing it. The only animation is when they choose to tap a different button within that view.
The reason you are experiencing this is that the actual view object and its presentation layer get mixed up when the view is hidden and redisplayed. The solution is to change the view properties rather than animate the layers.
Thus, you do not need to use affine transforms. Just set the new position of the pointer view directly by changing its frame or center.
The more up-to-date format to do this is with block based animations:
[UIView animateWithDuration:0.5 animations:^{
pointer.frame = newFrame;
}];
Also, it seems very inefficient to have three methods just differing by one variable. You could just multiply the desired y position by 80.
Edit:
Another reason your button resets is because that is where it was put when the view is instantiated. Just set the position of your view in viewWillAppear of your view controller and then animate it to the new location in viewDidAppear.
I appreciate the help from #Mundi but couldn't get it to work that way. I eventually arrived at the following code.
// This initially sets the frame with a variable value that
// corresponds to the current child view
-(void)viewDidAppear:(BOOL)animated {
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}
// Button actions (converted to a single action instead of one for each button)
// This was done by referencing a tag added to each button (the button tags
// match the children's tags)
-(IBAction)goToForm:(id)sender {
UIButton *btn = (UIButton *)sender;
self.currentChild = self.formViewControllers[btn.tag];
[UIView beginAnimations.nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}];
[UIView commitAnimations];
sfh.frame = selectedFormHighlight.frame;
}
// This returns the 'y' (vertical center position) of the pointer
// frame for each of the child views
-(int)getY {
switch(_currentChild.view.tag) {
case 1:
return -2005;
break;
case 2:
return -1925;
break;
default:
return -1845;
break;
}
}

Resources