I have a little message that appears when the app is conecting to the server.
It works nice when the app is launching, and also the rest of the time that the app needs to show this message, but if I sleep the app, I put it on the background and then I wake it up, the message appears again but the whole frame moves up, see screenshots.
The first one is right after launching, that message on the bottom appears and it dissapears correctly, and the view is back to what is used to be, perfect.
But the second screenshot is the result of making the app going to background, and then waking it up, on the "applicationDidBecomeActive" I have a method that requires conection to the server and therefore the small view must appear. The problem is that the window or the navigation controller moves up behind the status bar. And leaves that white space
Heres the code that I use to make shorter the navigation view and add that message view and make it longer again.
- (void)addMessageView:(NSString *)message{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
if(![window viewWithTag:3]){//Si no hay un messageView no lo meto.
[window addSubview:messageView];
CGRect frameWindow = window.frame;
int heightMessage = 50;
messageView.frame = CGRectMake(0, frameWindow.size.height, 320, heightMessage);
UINavigationController *nav = window.rootViewController;
//Pillo el UILabel y lo seteo
UILabel *label = (UILabel *)[messageView viewWithTag:1];
label.text = message;
CGSize maximumLabelSize = CGSizeMake(296,9999);
CGSize sizeMsg = [label.text sizeWithFont:label.font constrainedToSize:maximumLabelSize lineBreakMode:label.lineBreakMode];
UIActivityIndicatorView *activity = (UIActivityIndicatorView *)[messageView viewWithTag:2];
int xLabel = (320 - (sizeMsg.width + activity.frame.size.width + 5))/2;
CGRect frameActivity = activity.frame;
activity.frame = CGRectMake(xLabel, 13, frameActivity.size.width, frameActivity.size.height);
label.frame = CGRectMake(xLabel + activity.frame.size.width + 5, 15, sizeMsg.width, sizeMsg.height);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
nav.view.frame = CGRectMake(frameWindow.origin.x, frameWindow.origin.y, frameWindow.size.width, frameWindow.size.height - heightMessage);
messageView.frame = CGRectMake(0, frameWindow.size.height - heightMessage, 320, heightMessage);
[UIView commitAnimations];
}
}
AND also to put it back to the previous position:
- (void)hideMessageView{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
UINavigationController *nav = window.rootViewController;
CGRect frameWindow = window.frame;
if([window viewWithTag:3]){//Si no hay un messageView no lo meto.
[UIView animateWithDuration:0.3
animations:^{
nav.view.frame = CGRectMake(frameWindow.origin.x, frameWindow.origin.y, frameWindow.size.width, frameWindow.size.height);
messageView.frame = CGRectMake(0, frameWindow.size.height +5 , 320, 0);
}
completion:^(BOOL finished){
[messageView removeFromSuperview];
}];
}
NSLog(#"Tam Window: %# y nav %#", frameWindow, nav.view.frame);
}
Let me know what do you think.
Try disabling Autolayout, I had the same issue with my UI.
Related
I would like to generate a shockwave effect when I tap on a circular UIView. The effect I'm looking for is very similar to 'Circadia' on the App Store.
(source: mzstatic.com)
Note the circular line that is expanding from the centre view. I couldn't find any kind of tutorial, so I tried the effect using a cheat:
-(void)startShockwave:(UIView *)target
{
// Replace the view tapped with one that will expand
UIView *wave = [[UIView alloc]initWithFrame:target.frame];
wave.backgroundColor = target.backgroundColor;
wave.alpha = 0.5;
wave.layer.masksToBounds = YES;
wave.layer.cornerRadius = wave.frame.size.width / 2;
// Hide it below the original view
[self.view insertSubview:wave belowSubview:target];
CGRect frame = wave.frame;
// Create a view that is the same colour as self.view to make it look like a hole
UIView *center = [[UIView alloc]initWithFrame:CGRectMake(frame.origin.x + 10, frame.origin.y + 10, frame.size.width - 20, frame.size.height - 20)];
center.backgroundColor = self.view.backgroundColor;
center.layer.masksToBounds = YES;
center.layer.cornerRadius = center.frame.size.width / 2;
[self.view insertSubview:center aboveSubview:wave];
// Hide the original view
target.alpha = 0;
// IMPORTANT: I send these views behind the others so that center does not overlap them
[self.view sendSubviewToBack:center];
[self.view sendSubviewToBack:wave];
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
CGAffineTransform waveTransform = wave.transform;
CGAffineTransform centerTransform = center.transform;
// Expand the views to look like a shockwave
wave.transform = CGAffineTransformScale(waveTransform, 4, 4);
center.transform = CGAffineTransformScale(centerTransform, 5.75, 5.75);
// Fade the wave out to nothing
wave.alpha = 0;
} completion:^(BOOL finished) {
// Remove the shockwave
[wave removeFromSuperview];
[center removeFromSuperview];
}];
}
This works quite well...
...With only one shockwave. However, when they intersect, one center overlaps the other due to the most recent shockwave being sent to the back.
I would prefer intersecting shockwaves to be more like this:
I'm not sure how to create this type of effect, however, so any help is much appreciated!
Use [UIColor clearColor] to make the center of your circle transparent (so you can see interface elements residing beneath it).
center.backgroundColor = [UIColor clearColor];
It's a quick fix! You already did most of it. Just change this line:
center.backgroundColor = self.view.backgroundColor;
to
center.backgroundColor = [UIColor clearColor];
I want to create something that is basically a clone of what photosynth does for their tutorial page. A small "?" button pops up what looks like a new view in a frame that is slightly smaller than the first view, so that you can still see the first view around the edges.
It's a little tough to see from the pic above, but the part around the edges is the old view that the tutorial display popped up over.
My first guess is that I need to use a container view somehow, but I can't find anything on the web about exactly how to do this. I can currently create a container view, hook it up to a new view controller via a segue, and do whatever I want in that new view controller, but the container view is always visible on the view it is contained within. Any help?
BTW, I'm using storyboarding with ARC.
You can add a transparent view to the key window, add a tap gesture recognizer that would dismiss it and the subviews to show the content:
#define OVERLAY_TAG 997
-(void)showTutorial
{
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *overlay = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
overlay.backgroundColor = [UIColor clearColor];
overlay.userInteractionEnabled = YES;
[keyWindow addSubview:overlay];
UITapGestureRecognizer * tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissTutorial)];
CGFloat border = 10;
CGRect frame = overlay.bounds;
// 20 is the status bar height (sorry for using the number)
frame = CGRectMake(border, border + 20, frame.size.width - border * 2, frame.size.height - border * 2 - 20);
// the black view in the example is probably a scroll view
UIView *blackView = [[UIView alloc] initWithFrame:frame];
blackView.backgroundColor = [UIColor blackColor];
blackView.alpha = 0.0;
[overlay addSubview:dimView];
// add all the subviews for your tutorial
// make it appear with an animation
[UIView animateWithDuration:0.3
animations:^{dimView.alpha = 1;}
completion:^(BOOL finished){[overlay addGestureRecognizer:tapRecognizer];}];
}
-(void)dismissTutorial
{
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *overlay = [keyWindow viewWithTag:OVERLAY_TAG];
[UIView animateWithDuration:0.3
animations:^{
overlay.alpha = 0.0;
}
completion:^(BOOL finished){
[overlay removeFromSuperview];
}];
}
This way you would remove the tutorial with a simple tap but you can use a button for instance.
In my app I have a scrollview and when I press a button it is hidden when I press again it shows up.
I use scrollview.hidden = YES (or NO) to do it.
But I want to do it with an animation. For example, it may disappear from the bottom of the screen by moving and shows up with the same way. How can I do that?
edit:
[UIView beginAnimations:#"myAnimation" context:nil];
CGRect Frame = bottomScroller.frame;
if(Frame.origin.y == 380){
Frame.origin.y = 460;
}else{
Frame.origin.y = 380;
}
bottomScroller.frame = Frame;
[UIView commitAnimations];
This solved my problem...
You may check UIView animations. It's pretty easy. For example to animate translation you may use something like:
[UIView beginAnimations:#"myAnimation" context:nil];
CGRect Frame = yourView.frame;
Frame.origin.y = 0;
yourView.frame = Frame;
[UIView commitAnimations];
Then to move it back:
[UIView beginAnimations:#"myAnimation" context:nil];
CGRect Frame = yourView.frame;
Frame.origin.y = 100;
yourView.frame = Frame;
[UIView commitAnimations];
Changes in frame, alpha and some other parameters will be automatically animated.
You can use animations like so:-
[UIView animateWithDuration:2.0 animations:^{
[scrollview setframe:CGRectMake(xpos, ypos, width, height)];
}];
If you want to slide the scrollview on or off the screen set its y position - the bottom of the view is you want to hide it, the normal y position if you want to show it.
The 2.0 is an animation length, this can be changed to whatever you need it to be!
You can do this by using simple view animations. Here's an example of how you might do this:
// myScrollView is your scroll view
-(void)toggleShow{
CGFloat targetAlpha = myScrollView.alpha == 1 ? 0 : 1;
CGFloat yPosition = targetAlpha == 1 ? 0 : self.view.frame.size.height;
[UIView animateWithDuration:1.0 animations:^{
myScrollView.frame = CGRectMake(myScrollView.frame.origin.x, yPosition, myScrollView.frame.size.width, myScrollView.frame.size.height);
myScrollView.alpha = targetAlpha;
}];
}
targetAlpha will always be the opposite of the current state (1 or 0), and if it's 0 then the y position will be set to the bottom of the parent view. Using the new-school UIView animations API with blocks we can execute these changes to the scroll view over 1 second (in my example).
ScrollView appers from down side of screen with animation. I think this up-animation is what you want.
// ScrollView appearing animation
- (void)ScrollViewUpAnimation
{
// put the scroll view out of screen
CGRect frame = ScrollView.frame;
[self.view addSubview:ScrollView];
ScrollView.frame = CGRectMake(0, 460, frame.size.width, frame.size.height);
// setting for animation
CGPoint fromPt = ScrollView.layer.position;
CGPoint toPt = CGPointMake(fromPt.x, fromPt.y - frame.size.height - 44);
CABasicAnimation* anime =
[CABasicAnimation animationWithKeyPath:#"position"];
anime.duration = 0.2;
anime.fromValue = [NSValue valueWithCGPoint:fromPt];
anime.toValue = [NSValue valueWithCGPoint:toPt];
// change the position of Scroll View when animation start
[ScrollView.layer addAnimation:anime forKey:#"animatePosition"];
ScrollView.layer.position = toPt;
}
The following code is a method I created inside a UIViewController to popup/down a "reader" overlay on top of the controller's own view. The intention is for the reader to begin as transparent, size zero, at a specific point. "Popup" is then animated as increasing in opacity and size, and shifts towards an application frame central position. "Popdown" is subsequently animated as the reverse, shrinking back whilst moving toward a specified location, fading out.
The popup code works exactly as desired. However, the popdown version (i.e. code executed if isPopup == NO) immediately changes the bounds rather than doing so gradually. Thus the popdown animation shows from the beginning a 1 pixel square view moving towards its destination and fading out.
-(void)popupReader:(BOOL)isPopup from:(CGPoint)loc {
CGFloat newAlpha = 0.0f;
CGPoint newCenter = CGPointZero;
CGRect newBounds = CGRectZero;
CGRect appFrame = [UIScreen mainScreen].applicationFrame;
CGSize readerSize = [self viewSize];
if (isPopup) {
newAlpha = 1.0f;
newCenter = CGPointMake(appFrame.origin.x+appFrame.size.width/2,
appFrame.origin.y+appFrame.size.height/2);
newBounds = CGRectMake(0,0,readerSize.width,readerSize.height);
[self.view setAlpha:0.0f];
[self.view setCenter:loc];
[self.view setBounds:CGRectMake(0, 0, 0, 0)];
} else {
newCenter = loc;
newBounds = CGRectMake(0,0,1,1);
}
const CGFloat animDur = 0.3f;
[UIView transitionWithView:self.view
duration:animDur
options:UIViewAnimationOptionTransitionNone|UIViewAnimationOptionCurveEaseOut
animations:^{
self.view.alpha = newAlpha;
self.view.center = newCenter;
self.view.bounds = newBounds;
}
completion:nil];
}
I've already tried animating just the frame, rather than bounds and center, but the result was identical.
Does anyone know why this is happening, and how I can overcome this problem?
Many thanks for your time.
from the docs:
UIViewAnimationOptionTransitionNone -
An option for specifying that no transition should occur.
try one of:
UIViewAnimationOptionTransitionFlipFromLeft
UIViewAnimationOptionTransitionFlipFromRight
UIViewAnimationOptionTransitionCurlUp
UIViewAnimationOptionTransitionCurlDown
Edit:
Or if you're just looking to animate those values, try
[UIView animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion];
I want to slide in a UIDatePicker when my user taps on my table view date field, exactly as in the standard contact app, when the user taps on the birthday field. With one additional killer detail:
It's in a tab bar application and I want the UIDatePicker to slide over the tab bar. And still rotates when the user puts her phone in the lanscape orientation.
The way I show the UIDatePicker is to insert in the view, and then animate its position:
[self.view addSubview: self.pickerView];
When I do this, the UIDatePicker is shown, but doesn't cover the tabbar.
I can also add it to the window:
[self.view.window addSubview: self.pickerView];
The UIDatePicker correctly slides over the tab bar, but then, it doesn't follow the orientation.
The only semi-acceptable way I found, is to do without the tab bar altogether, by using
detailViewController.hidesBottomBarWhenPushed = YES;
But it's not what I want: I want the tab bar in the detail view. I only want it to go away while picking the date. I can't put the UIDatePicker in its own controller with hidesBottomBarWhenPushed = YES as this would entirely cover the screen, and I'd rather have the detail view still partially visible.
Any suggestion welcome.
Here is the full code I use to show the UIDatePicker, copied over from some sample code:
- (void) doPickDate
{
NSDate *initialDateForPicker = [(ExpenseData*) self.displayedObject displayDate];
self.pickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
self.pickerView.datePickerMode = UIDatePickerModeDate;
self.pickerView.date = initialDateForPicker;
// check if our date picker is already on screen
if (self.pickerView.superview == nil)
{
[self.view addSubview: self.pickerView];
// size up the picker view to our screen and compute the start/end frame origin for our slide up animation
// compute the start frame
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
CGSize pickerSize = [self.pickerView sizeThatFits:CGSizeZero];
CGRect startRect = CGRectMake(0.0,
screenRect.origin.y + screenRect.size.height,
pickerSize.width, pickerSize.height);
self.pickerView.frame = startRect;
// compute the end frame
CGRect pickerRect = CGRectMake(0.0,
screenRect.origin.y + screenRect.size.height - pickerSize.height,
pickerSize.width,
pickerSize.height);
// start the slide up animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
// we need to perform some post operations after the animation is complete
[UIView setAnimationDelegate:self];
self.pickerView.frame = pickerRect;
// shrink the table vertical size to make room for the date picker
CGRect newFrame = self.tableView.frame;
newFrame.size.height -= self.pickerView.frame.size.height - 49 /* tab bar height */;
self.tableView.frame = newFrame;
[UIView commitAnimations];
// add the "Done" button to the nav bar
self.navigationItem.rightBarButtonItem = self.doneButton;
}
}
You should set the UIDatePicker object as the inputView of that particular text field. All the animations and presentation stuff will be taken care of.
Hiding the cursor
There is no method to hide the cursor. The only mechanism I could think of was to use the leftView property and set it to a label.
CGRect frame = self.textField.bounds;
UILabel * theLabel = [[[UILabel alloc] initWithFrame:frame] autorelease];
theLabel.userInteractionEnabled = YES;
theLabel.backgroundColor = [UIColor clearColor];
UITapGestureRecognizer * tap = [[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tap:)] autorelease];
[theLabel addGestureRecognizer:tap];
self.textField.leftViewMode = UITextFieldViewModeAlways;
self.textField.leftView = theLabel;
self.textField.clipsToBounds = YES;
and handle the tap using,
- (void)tap:(UIGestureRecognizer *)gesture {
[self.textField becomeFirstResponder];
}
Now this doesn't work for the rounded rect button as the cursor blinks on the right corner no matter what the label's frame is. It hides the cursor for other border styles. You can also use the label to your advantage by setting your values as its text.
UILabel * theLabel = textField.leftView;
theLabel.text = #"Appropriate Value from Picker";