UiView fadeOut if not touched - objective-c

I'd like to reproduce this behavior in my iPad application.
I have a subView that contains four custom buttons.
The view has an alpha value of 0.0
I have another custom button outside of the view descripted above that is always visible.
When the user touches the visible button the view appear animating its alpha to 1.0 showing the other 4 buttons.
Now I'like to start a timer that fires the view fadeOut after 2 seconds
When and if the user interact (say touchDown or whatever) with the buttons the timers need to be reset.
In other words the view may disappear only when nobody touches a button inside It.
Can you help me with this.
I've managed to learn the basics of UIView animation but I don't know how to queue them.
My iPad has iOS 3.2.2. installed.
Sorry for the bad explanation but this is my first iPad application and my first obj-c project.

You'd keep an NSTimer instance variable for that. As soon as your view is completely visible, which you can notice by e.g. implementing the fade in animation's delegate, you initialize it like this:
_fadeTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(fade:) userInfo:nil repeats:NO];
Make sure _fadeTimer is an instance variable, you need to be able to reset it. Then implement the fade out method:
- (void)fade:(NSTimer *)aTimer {
// Forget about timer!
_fadeTimer = nil;
[UIView beginAnimations:nil context:NULL];
// fade here
[UIView commitAnimations];
}
Upon every user interaction you just call a method that delays the fade. To do this, delete and re-create the timer. Or change it's fire date:
- (void)delayFade {
[_fadeTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow: 2.0]];
}
PS: There is no need to explicitly retain the timer. It's retained by the runloop until it fires. After the callback, it will be released anyways. Just make sure you always reset the variable to nil, otherwise your app may crash on an invalid access. If you need to delete the time beofre it fired, call the invalidate method.

Related

xcode UIbutton setcenter does not move button

I am using xcode and am having a problem moving a button automatically. I have this function that whenever I call it, I expect the button to move to the coordinates that I set:
[movebutton setCenter:CGPointMake(164,50)];
Previously I tried to set an NStimer in an IBAction function and then use the timer to call this movebutton function - the button moved but if I call the same function without an NStimer it no longer works.
The code for the timer is:
[NSTimer scheduledTimerWithTimeInterval:(0.1/3) target:self selector:#selector(movebutton:) userInfo:nil repeats:YES];
Can anyone spot what I am missing?
Update:
I try to print out the x and y coordinates of the button and actually the button position has been updated.
NSLog(#"x position %f",movebutton.frame.origin.x);
however, on the UI screen it does not reflect at all.
Try placing
[self.view layoutIfNeeded];
in your moveButton: method so that when it's called from the timer it knows to update the UI.
Where do you call -[UIButton setCenter] manually? Couldn't comment because I don't have permission to comment.
The NSTimer works because it fires the method every 0.1/3 because the NSTimer instance is added to run loop

NSMenuItem custom view not updating

I have a NSStatusItem object which is created when the app launches (in AppDelegate.m). This object "_statusBarItem" has a Menu attached to it, of class statusBarMenu, which is subclass of NSMenu class but it also has a _panelItem property (of class NSMenuItem) created when you create an instance of statusBarMenu, as you can see below.
In statusBarItem.m
- (instancetype)initWithTitle:(NSString *)aTitle{
self = [super initWithTitle:aTitle];
if (self){
_panelItem = [[NSMenuItem alloc]init];
PanelViewController *panelViewController = [[PanelViewController alloc]init];
panelViewController.menuDelegate = self;
_panelItem.view = panelViewController.view;
[self addItem:_panelItem];
}
return self;
}
The _panelItem has a custom view i.e. a clock in a label (among other things). This view is controlled by PanelViewController class, in which when viewDidLoad method is called calls the tickTock: method shown below. _upTime is the label showing, the clock/time. It is created in the .xib file and connected
- (void)tickTock:(id)obj{
NSTimeInterval timeInterval = 7.5 * 60 * 60;
NSDate *timeToGetUp = [NSDate dateWithTimeInterval:timeInterval sinceDate:[NSDate date]];
NSDateFormatter *dateFormatter = [NSDateFormatter new];
[dateFormatter setDateFormat:#"hh:mm:ss"];
[_upTime setStringValue:[dateFormatter stringFromDate:timeToGetUp]];
[self.view setNeedsDisplay:YES];
[self.menuDelegate refreshView:self];
[self performSelector:#selector(tickTock:) withObject:NULL afterDelay:1.0];
}
As you can see that tickTock: method is called every 1.0 second. This is because I want the label to update, every second with new time. However, the label does not update even though I call setNeedsDisplay: for the PanelViewController's view. I thought this might be because I might be updating the wrong view i.e. I should have been updating the _panelItem's view, instead. So I made a menuDelegate property and made statusBarMenu conform to the protocol show below.
#protocol PanelViewControllerMenuDelgate
- (void)refreshView:(id)obj;
#end
Again the refreshView: method is called every second, it updates the panel view.
- (void)refreshView:(id)obj{
[_panelItem.view setNeedsDisplay:YES];
// [self itemChanged:_panelItem];
}
However, that still does not refresh the view, and show the new label value. I also tried itemChanged: method to the statusBarMenu obj (_statusBarItem) itself, although it did not have any different results.
What I did notice is that if I close the menu (by clicking somewhere else) and re-open it, it does show the new value of the clock/label. So what am I doing wrong, or what am I not doing, that is making the view stay the same. Should I be using some other procedure to make the _panelItem's view refresh every second?
EDIT:
I also noticed that the simulation stop running, when every click the _statusBarItem. I simply added a NSLog statement in tickTock: method and it stopped printing in the console, when ever I open the menu. This is probably why the view is not updating, since the app/Xcode pauses when ever I click the menu. Why does that happen? Also how can I stop it?
I just came up against this problem a few weeks ago. In my case, my NSTimer was not updating my NSMenuItem. I believe your problem is happening because the menu is updating on a different run loop to the perform with delay. This question is answered in a few places. But this is the code you need:
NSTimer interfaceUpdateTimer = [NSTimer timerWithTimeInterval:self.timerSettings.timerUpdateInterval//one second in your case
target:self
selector:#selector(tickTock:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:interfaceUpdateTimer forMode:NSRunLoopCommonModes];
The key is NSRunLoopCommonModes. Check out the other answers I linked to; they already explain it really well. That should do the trick I think.
As a side note, neither the NSTimer nor the performSelector:withObject:afterDelay: are guaranteed to fire at exactly the time you specify. So if you need accuracy, do not rely on them to tell the time. If close enough is good enough, then you can ignore this note : )

App crashes if view controller transition is done too quickly

I am using view controller containment to transition between 6 view controllers. Transitions are controlled using a segmented control. This all works fine unless buttons on the segmented control are pushed before animation of the previous transition has completed. In this situation, the app crashes with
'Children view controllers and must have a common parent view controller when calling -[UIViewController transitionFromViewController:toViewController:duration:options:animations:completion:]'
Code is:
[self transitionFromViewController:currentVC
toViewController:newVC
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
animations:nil
completion:^(BOOL finished) {
[currentVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
currentVC = newVC;
}];
Should I disable the segmented control until animation is completed? Or is their a better way to avoid this issue?
You can disable and reenable application interaction by calling
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
when the animation starts and ends respectively. The app will then ignore all interaction (touch events) until the animation is finished, so the segment will never receive event before it is safe (animations are done).
I think this approach is used on some built-in container controllers as well. However be careful about animation duration. If the animation will take a long time, it can look like the app is not responding well, which hurts user experience

Refresh NSImageView while NSWindow is being dragged around

I've got a NSTimer which fires every few milliseconds to load and then assign a new image into a NSImageView. The NSTimer has been setup using various run loops (NSRunLoopCommonModes, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode) just so that it continues to do what it's doing when events are firing.
This works great however this ceases to work when the actual window is being dragged around the screen. I have confirmed that the timer is in fact firing during the drag, and the image is being assigned to the NSImageView as well. The only problem is that the screen contents don't refresh for some reason when the window is being dragged. This is possibly a built-in optimization but I'd like to disable this so that the imageview continues to refresh and re-draw even when the window is being dragged around.
I've tried the following inside the NSTimer firing block but this still did not force the contents of the NSImageView to be refreshed and redrawn:
- (void)animateTimerFired:(NSTimer *)aTimer {
// .... load imageObj
// set image
[imgBackgroundView setImage: imageObj];
// try and refresh the window forcibly
[[imgBackgroundView window] viewsNeedDisplay];
[[imgBackgroundView window] display];
[[imgBackgroundView window] update];
[[imgBackgroundView window] flushWindow];
}
Is there any way to disable this 'optimization'?

Adding a SubView does not display before pushViewController

Similar to this question: Adding subview, gets delayed?
But I don't think you can pushViewController in a separate thread so is this really impossible?
Here is what I'm trying to do:
I have a TableView and when a cell is pressed, I want to call
[self.view addSubview:LoadingView]
to display an overlay with a spinner. Then I call
self.navigationController.navigationBar.hidden = NO;
[self.navigationController pushViewController:newGameViewController animated:YES];
However, the subview only displays for a split second (~1-4 seconds after the cell selection occurs while it waits for the new viewcontroller to initialize).
Is there any way to get some sort of loading indicator to occur at the instant the cell is selected?
Okay. What about this. In your didSelectRowAtIndexPath start your spinner (via addSubview ...) and start loading your stuff from the server. If that's finished remove the spinner an push your new view controller onto the stack. Make sure the user can't touch any other cell during that time. By the way. From a users perspective I'd find it mor intuitive if the new controller is loaded immediately and displays some waiting message.
Or the other way around: The newGameViewController displays the spinner and starts loading the data from the server in a background thread. When the data is complete, remove the spinner and display the data. That way the user could even go back if she doesn't want to wait.
You do not need to add code before pushing newGameViewController.
Inside viewDidLoad of newGameViewController, write the code of displaying spinner. To get the updated UI, just insert a delay before calling a web API.
inside GameViewController.m
-(void) viewDidLoad
{
[self.view addSubview:LoadingView];
[self performSelector:#selector(callWebAPI) afterDelay:0.1];
}
-(void) callWebAPI
{
//Handle network activity here..
}