I have a UIViewController subclass that changes its frame when the UI rotates (for example, most commonly, the frame is the same size and centered in both landscape and portrait.) When the view rotates, the view visually changes its frame as expected. But the controls (buttons, scroll views, etc.) that end up in the area of the new frame do not respond to touches. The only controls that respond are the ones that have remained in the original frame (so, if the view moves down and to the left, controls or parts of controls in the upper right corner remain responsive.) Any idea what's going on here?
Here's the code that changes the frame:
- (void) setLandscape:(bool)bLandscape {
if( bLandscape )
[self setFrame:m_landscapeFrame];
else
[self setFrame:m_portraitFrame];
}
And in the bigger view controller that handles rotation directly:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
bool bToLandscape= UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
[UIView beginAnimations:NULL context:nil];
[UIView setAnimationDuration:duration];
[m_pCurrentWindowViewController setLandscape:bToLandscape];
// (Some unrelated stuff happens here...)
[UIView commitAnimations];
}
To me this seems like a bug or at least poor design on Apple's part - there should never be a time when a view is in one position visually and another position in terms of control. But I'm just looking for a way to fix this behavior.
The simplest workaround I found was to update the frame in didRotateFromInterfaceOrientation after animating in willRotateToInterfaceOrientation.
Related
I have an app which intakes client details, built for iPad. When the user taps a UITextField towards the bottom half of the ViewController, the frame programatically shifts upwards so that the fields aren't hidden behind the keyboard. (I tried to implement a UIScrollView but just cannot seem to get it working.) The slight issue I'm having now is when the frame shifts up, you can vaguely see the black behind it. This isn't a huge issue because I have changed the animation time and the black background is barely visible, but I have a feeling there is a more elegant solution.
Here is my code to shift the frame:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// Animate frame up if lower textfields are tapped
if (([textField isEqual:_emailField]) || ([textField isEqual:_dobField]) || ([textField isEqual:_niNumField])) {
[UIView beginAnimations:nil
context:NULL];
[UIView setAnimationDuration:0.35f];
CGRect frame = self.view.frame;
frame.origin.y = -210;
[self.view setFrame:frame];
[UIView commitAnimations];
} else {
// Return frame to original position if other textfields are tapped
[self dismissKeyboard];
}
return YES;
}
- (void)dismissKeyboard {
[UIView beginAnimations:nil
context:NULL];
[UIView setAnimationDuration:0.15f];
CGRect frame = self.view.frame;
frame.origin.y = 0;
[self.view setFrame:frame];
[UIView commitAnimations];
}
Here is a brief screenshot of what I'm trying to describe. Left picture is the ViewController normally, right picture is when the frame has been shifted upwards. You can see (vaguely) almost in line with the second row of characters is where the frame stops.
I realise this doesn't seem like an issue because it is barely visible at all, but I have had to speed up the animation hiding the keyboard or else the frame drags behind and the black background becomes visible. I am wondering: is there a way to change this colour? Or is that something we don't have access to? If anyone can suggest a more elegant method for what I am trying to do, I'd gladly take a better solution. Thanks in advance.
Your UIViewController's view is added to the main UIWindow. So you should be able to achieve what you want by changing the UIWindow's background color.
In the UIApplicationDelegate:
self.window.backgroundColor = [UIColor whiteColor];
However, what you're doing isn't the best way to solve the problem of the keyboard covering up the text fields. You should use a UIScrollView or a UITableView to manage this view and use content insets to shift the view up or down.
I have two UIViews. I'm using one to contain the other so that I can slide one inside the other. I'm encountering an issue where even though a subView is clipped to the bounds of its parent, it is still receiving touch events and blocking access to other underlying views.
I have three screenshots that show the layout. I've coloured the parent green and the child red.
The idea is that the user clicks "View" and the subView slides up. When the subView is in the default position, the UITabBar is covered and cannot be clicked. You can see this in the first image where the red view is present at the bottom. When the subView is moved to the top, the UITabBar can be clicked as it's now visible. In the third image, I've show what it's like with clipToBounds enabled on the green UIView.
I've enabled clipToBounds, so I cannot understand why the subView is blocking the underlying UITabBar. Is my understanding of clipToBounds completely wrong??
Using clipToBounds only affects the visual layout of a subView, not the logical layout. This means that whilst my subView isn't visible to the eye, it's visible to touch.
I've worked around this issue by animating the size of the subView rather than its position. In my code below, the stationProximityView is the subView. I animate its size by 40 pixels to bring the black title back into view.
[UIView beginAnimations:#"stationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectOffset(self.stationProximityView.view.frame, 0, -40);
[UIView commitAnimations];
When I no longer need it, I animate it out of view.
[UIView beginAnimations:#"stationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectMake(0 ,0, 320, 500);
[UIView commitAnimations];
If the user taps the view button, the entire subView is shown:
[UIView beginAnimations:#"stationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectMake(0,460,320,40);
[UIView commitAnimations];
Dismissal causes the view to be hidden in the same way as the small bar.
[UIView beginAnimations:#"hideStationProximityBar" context:NULL];
self.stationProximityView.view.frame = CGRectMake(0,0,320,500);
[UIView commitAnimations];
At the moment, this code is only being tested on the iPhone 5, so the hard-coded height of 500 would causes issues on previous iPhone models.
I have a UIViewController which has multiple subviews. Each subview is a UIView subclass, and I want to switch between views by tapping the toolbar buttons. I did this by using the animation blocks:
Example:
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[StoreView removeFromSuperview];
[self.view addSubview:HomeView];
}
completion:NULL];
Everything is working fine actually. The problem is the transition is not really smooth. For example, the HomeView has five buttons scattered (as part of the design), and whenever I switch from one view to HomeView, these buttons will come from a corner and rearrange itself after the transition, which is not exactly beautiful to look at.
So how will I make these buttons stay in place?
When doing animations with complex subviews you can sometimes get undesirable results as you are experiencing. Not only can some oddities appear, but they are sometimes costly depending on the complexity of the view structure. One suggestion I would make is instead of animating the complex views themselves, you could render the views to a graphics context and animate the resulting image in a UIImageView, using sleight-of-hand to make it appear that you are animating the view hierarchy. In this effect, you avoid doing what amounts to a complex transform on the HomeView and StoreView and instead do simple flip with UIImageView instances. Consider the following example code:
UIImageView *storeImage = // pointer to the image rendered to a graphics context
UIImageView *homeImage = // pointer to the image rendered to a graphics context
[self.view addSubview:storeImage];
[storeView removeFromSuperview];
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[storeImage removeFromSuperview];
[self.view addSubview:homeImage];
}
completion:^(BOOL finished) {
[self.view addSubview:homeView];
[homeImage removeFromSuperview];
}];
I have a view interface which contains a subview touchWheel. On clicking a minimise button on interface I'd like to perform a transform & send the view to the bottom-right corner of my screen. The thing is that I want to "detach" the touchWheel view & send it to the corner separately with the parent-view, interface, following behind (& in fact fading out).
when I try this I encounter a problem. My animation works fine initially & touchWheel is sent towards the bottom corner, as desired. When touchWheel is about half-way to its destination I animate interface so that it follows touchWheel.
Once interface starts animating it appears to control touchWheel & touchWheel changes course.
It seems that interface still retains control of touchWheel since it's its parent view.
- (void)hideInterfaceButtonClicked : (id) sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"RemoveGrey" object:self];
//Remove Views & transform
[touchWheel removeViews];
interfaceHidden = YES;
//Send Interface to corner
[UIView animateWithDuration:1.25
delay:0.0
options:UIViewAnimationCurveEaseIn
animations:^{
// Move to the right
CGAffineTransform translateInterface = CGAffineTransformMakeTranslation(437,200);
// Scale
CGAffineTransform scale = CGAffineTransformMakeScale(0.135,0.135);
// Apply them to the view
self.transform = CGAffineTransformConcat(scale, translateInterface);
self.alpha = 0.0;
} completion:^(BOOL finished) {
NSLog(#"Animation Has Stopped");
[[NSNotificationCenter defaultCenter] postNotificationName:#"hiddenInterfaceViewNeeded" object:self]; //after MoveView finishes
}];
}
Making touchWheel subview of something other than interface creates other problems which would complicate things further.
Any ideas how one might get around this issue? any tips greatly appreciated :)
You could do the following:
Move touchWheel to the interface's superview, recalculating its frame using convertRect:toView: so that touchWheel appears to have the same window coordinates.
Animate both the views to their final locations.
In the completion block of the animation do the opposite of step 1: make touchWheel a subview of interface, recalculating its frame again.
It doesn't seem too complicated, but there's a caveat: you may want to temporarily disable the maximize button (or whatever the appropriate control is) to prevent the maximize action during the animation. Otherwise, this approach will cause unpredictable results.
I'd like to "pulse" the stroke color on a path that I've drawn in a UIView's drawRect. But not sure this is possible?
- (void)highlightView {
if (!isHighlighted) {
[UIView beginAnimations:NULL context:nil];
[UIView setAnimationDuration:0.75];
self.strokeColor = kHighlightColor;
self.strokeWidth = 2.0;
[UIView commitAnimations];
self.isHighlighted = YES;
}
}
I'm only able to see the change if i setNeedsDisplay on the view. But that bypasses the animation. I can think of a few workarounds if this isn't possible like overlaying another semi-transparent view with the proper color and just fading it in... but for some reason I thought animating color properties was possible in Cocoa?!? Perhaps I'm mistaken. Hopefully one of you can set me straight.
You absolutely can animate colour properties with UIView, but it doesn't really make sense in the context you're dealing with right now.
In drawRect: you are essentially painting a colour when you stroke a path, those bits just get blitted to the screen. At that point, the colour isn't really a property of the view so much as part of the painting.
I wrote a UIView that pulsed this summer (emulating the "In Call" status bar) by creating two UIViews stack atop each other, and animating both of their color properties.