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'?
Related
I have implemented pull to refresh in a table view that is a subview to my main view like so:
UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.tableView;
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;
This updates the table and everything fine, the problem I'm having is when I touch the table to drag the cells seem to randomly shift up above the screen. This happens when I touch and drag from the middle of the screen down, or if I pull before the the cells shift back to their normal position. Here are some screen shots to better paint a picture. Keep in mind that all of these are taken after I drag down, yet the cells shift upward.
Should look like:
actually looks like these after the cells jump:
For anyone that may be having this issue or an issue like this I found a way to get the scroll to refresh to work very smoothly and not cause the issues I was having. I'm using an actual UITableViewController now instead of an embedded UITableView which helped with some of the scroll issues, but I still had an issue where instead of animating the closing of the refresh icon (table sliding up to cover the spinning circle) it just shut instantly and seemed very jarring. I put my refresh logic in the background, let it finish and then finished refreshing. My code structure is as follows:
- (void)viewDidLoad {
//set up refreshcontrol to call my refresh method
}
- (void)refresh {
[self performSelectorInBackground:#selector(getUpdatedInfo) withObject:nil];
}
Updated info runs and my TableViewController is a delegate of my class that gets refreshed information. When all the information is received and the update is completed I call the delegate method in my refreshing TableViewController class that puts the received array into the table, updates the table, and ends refreshing.
- (void)didFinishingUpdatingWithArray:(NSArray *)array {
//configure table sections
//Save the data
[self.tableview reloadData];
[self.refreshControl endRefreshing];
}
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..
}
I'm having trouble resizing an NSWindow to fit an NSView. It must be a logic error as it works but not for one action.
I have one NSWindow which is empty, and 3 NSViews with components and are different sizes.
With the following code I resize the NSWindow to fit the NSView and display it:
[_window setContentSize:_mainView.frame.size];
[_window setContentView:_mainView];
This code works fine.
However in one NSView I have a Back button, and while this displays the correct NSView in the NSWindow, it does not re-size it back. As an example the initial window is a certain size, I click to switch to another view and it resizes correctly, I press the back button, the NSView is displayed but the window stays the same size?
Can anyone explain to me why when I switch back to the original NSView, it doesn't resize the NSWindow?
Thanks in advance everyone. This is the complete code I have:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[_window setContentSize:_mainView.frame.size];
[_window setContentView:_mainView];
}
- (IBAction)switchSubtractionView:(id)sender {
[_window setContentSize:_subtractionView.frame.size];
[_window setContentView:_subtractionView];
}
- (IBAction)switchAdditionView:(id)sender {
[_window setContentSize:_additionView.frame.size];
[_window setContentView:_additionView];
}
// -----------------------------------------------------------------------
// THE FOLLOWING METHOD DISPLAYS THE NEW VIEW CORRECTLY BUT DOESN'T RESIZE
// -----------------------------------------------------------------------
- (IBAction)switchMainMenu:(id)sender {
[_window setContentSize:_mainView.frame.size];
[_window setContentView:_mainView];
}
Thanks in advance everyone.
EDIT: it seems to me that when getting VIEW.frame.size, if this is repeatedly used, it loses its values? This seems very strange behaviour to me?
A window's content view must always be sized to fill the window's content area. Therefore, when you set the window's content size, you effectively change the size of the current content view. This happens just before you switch the content view, so you are changing the size of the old view.
Try setting the content view to a new, disposable NSView before changing the content size, and then setting the new content view.
I'm making a game that involves buttons moving across the screen. When one button reaches the edge of the screen without being tapped, you lose a bar of health.
-(void) moveStickFig:(NSTimer *)timer {
UIButton *stick = (UIButton *)timer.userInfo;
CGPoint oldPosition = stick.center;
stick.center = CGPointMake(oldPosition.x + 1 , oldPosition.y);
if (oldPosition.x == 900) {
[stick removeFromSuperview];
healthCount--;
NSLog(#"%d", healthCount);
}
}
When you click on a button it disapears using [btn removeFromSuperview] The problem with this is the button still exists and continues moving across the screen. Is there a way to delete it completely? I've tried [stick release] but for some reason it just causes the app to freeze
It looks like you're using a repeating timer to move the button. If you don't explicitly end that timer, the timer is going to keep running, and moving the button.
Normally when you send the removeFromSuperview message to something like a button, it would deallocate or "delete" that object. This is because when the button is added to the superview, the superview retains the button, giving it a retain count of 1, and when it's removed from the superview, it releases it, giving it a retain count of 0.
However, because the button is stored as the userInfo of the timer, the timer also retains the object giving it a retain count of 2, and after you remove it from the superview it still has a retain count of 1. If you simply send the release message to the button, it will lower the retain count to 0 and it will deallocate the button, but it won't stop the timer. The next time the timer runs, it will cause problems because you're trying to access deallocated memory.
What you really want to do is to invalidate the timer: [timer invalidate]. This will stop the timer, and the timer will send a release message to the button, causing the button to be deallocated.
NSTimer retains its userInfo, which is the button object in your case. You should kill the timer using [timer invalidate].
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.