Subview presentation delayed - objective-c

In my UITableViewController subclass I want to prepare the tabular data and reload the table after the view has already appeared (the table therefore initially loads empty). In my app delegate I have methods to produce and remove an activity screen. My problem seems to be that the activity view presentation is being delayed until after the reloadData call has been made. I proved this by removing the hideActivity line and sure enough my activity view appeared simultaneously with the reloading of the table. Here's the viewDidAppear for my view controller...
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[(AppDelegate *)[[UIApplication sharedApplication] delegate] showActivity];
[self prepare];
[self.tableView reloadData];
[(AppDelegate *)[[UIApplication sharedApplication] delegate] hideActivity];
}
I'm assuming this may have to do with the views not redrawing mid-method, but don't remember this happening before. Any ideas?
...although I know they work, here's my activity methods. Maybe the way I'm doing these allows for some sort of delayed appearance...
- (void)showActivity
{
self.activityView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
self.activityView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.75f];
UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake((320.0f / 2.0f) - (37.0f / 2.0f), (480.0f / 2.0f) - (37.0f / 2.0f) - (20.0f / 2.0f), 37.0f, 37.0f)] autorelease];
spinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[spinner startAnimating];
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(0.0f, spinner.frame.origin.y + spinner.frame.size.height + 10.0f, 320.0f, 30.0f)] autorelease];
label.textAlignment = UITextAlignmentCenter;
label.text = #"Working";
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor clearColor];
[self.activityView addSubview:label];
[self.activityView addSubview:spinner];
[self.window addSubview:self.activityView];
}
- (void)hideActivity
{
[self.activityView removeFromSuperview];
[self.activityView release];
}

Your UI isn't updating until you return control to the runloop. You need to either perform the preparation of the data in a background thread, or return control to the runloop to update the UI before starting the preparation, like this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[(AppDelegate *)[[UIApplication sharedApplication] delegate] showActivity];
[self performSelector:#selector(prepareData) withObject:nil afterDelay:0.0];
}
- (void)prepareData {
[self prepare];
[self.tableView reloadData];
[(AppDelegate *)[[UIApplication sharedApplication] delegate] hideActivity];
}

Related

Cocoa NSAnimationContex Weird behaviour

I have an NSView NSButton and an NSTextField in this view's subviews
all of them are Layer Backed
I ma trying to animate this view changing frame.
- (void)animateTheView:(SPTabView *)view toFrame:(NSRect)endFrame
{
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context)
{
context.allowsImplicitAnimation = YES;
context.duration = ANIMATION_TIME;//is a define 0.6f
[view setFrame:endFrame];
} completionHandler:nil];
}
Edit In my view I am not using AutoLayout I am just overriding the setFrame:
- (void)setFrame:(NSRect)frame
{
[super setFrame:frame];
[_titleLabel setFrame:self.bounds]; //The NSTextField I am talking about
[_closeButton setFrame:NSMakeRect(5, 2, B_WIDTH, B_HEIGHT)];//This one works fine
[self removeTrackingRect:_tracking];
_tracking = [self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:NO];
}
EDIT 2 Posting the subViews Initialisation Code
[super setWantsLayer:YES];
[self setWantsLayer:YES];
[self setCanDrawSubviewsIntoLayer:YES];
[self drawRegular];
_closeButton = [[NSButton alloc]init];
[_closeButton setCanDrawSubviewsIntoLayer:YES];
[_closeButton setTitle:#"X"];
[_closeButton setTarget:self];
[_closeButton setAction:#selector(closeTab)];
[_closeButton setHidden:YES];
[_closeButton setWantsLayer:YES];
_titleLabel = [[SPLabel alloc]init]; //SPLabel is a silly subclass of NSTextField which just set it's properties like editable to as NO
[_titleLabel setText:#"Untitled.txt"];
[_titleLabel setTextColor:[NSColor blackColor]];
[_titleLabel setAlignment:NSCenterTextAlignment];
[_titleLabel setWantsLayer:YES];
[_titleLabel setTag:6];
[self addSubview:_titleLabel];
[self addSubview:_closeButton];
My view and it's button animating FINE! but the textField doesn't animate at all - it just jumps to the EndFrame...
Any Ideas some1 ?

UIButton as a subview in MKAnnotationView doesn't work

I have added a button to my annotation view, and it appears correctly. When the button is tapped, I want a call to be made, but when I tap the button, it doesn't respond (the button doesn't even highlight). It seems that the button doesn't receive the touch event, but I don't know why.
Here is my code in customAnnotationView.m
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
NSLog(#"initWithAnnotation start");
//initialze the view
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self != nil)
{
CGRect frame = self.frame;
frame.size = CGSizeMake(60.0, 85.0);
self.frame = frame;
self.backgroundColor = [UIColor clearColor];
self.centerOffset = CGPointMake(30.0, 42.0);
}
//this has nothing to do with my question
self.title=[[[NSString alloc] init] autorelease];
self.description=[[[NSString alloc] init] autorelease];
self.phoneNumber=[[[NSString alloc] init] autorelease];
customAnnotation * tempCust = (customAnnotation *)annotation;
self.title=annotation.title;
self.description=tempCust.description;
self.phoneNumber=tempCust.phoneNumber;
//here is the question button
self.userInteractionEnabled=YES;
self.exclusiveTouch=NO;
UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Call" forState:UIControlStateNormal];
//decide the button's position
CGRect rect=button.frame;
rect.origin.x=15;
rect.origin.y=47;
rect.size.width=40;
rect.size.height=20;
button.frame=rect;
[self addSubview:button];
[button becomeFirstResponder];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
NSLog(#"initWithAnnotation finished");
return self;
}
//action method
-(void)buttonClicked:(id)sender{
NSString *callNumber=[[[NSString alloc] initWithFormat:#"tel://%#",self.phoneNumber] autorelease];
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:callNumber]];
}
Why doesn't the button work?
self.rightCalloutAccessoryView = button;
Use the above instead of [self addSubview:button];
Hope this help's you

change UITableView setBackgroundColor if UISearchBar is populated

If anyone could help me with this, that would be great. Thanks in advance Max
I want to change UITableView setBackgroundColor from clearColor to whiteColor if text is entered in UISearchBar .
i.e. the placeholder text disappears.
Immediately when text is deleted i.e. placeholder text reappears
the UITableView setBackgroundColor resumes to clearColor.
CGRect frame = CGRectMake(0, 140, 320, 170);
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(13, 100, 300, 40)];
[searchBar setDelegate:self];
[searchBar setPlaceholder:#"Species/Common Name"];
[searchBar setShowsCancelButton:YES];
[searchBar setTintColor:kITBarTint];
[self.view addSubview:searchBar];
self.tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain];
[self.tableView setBackgroundColor:[UIColor clearColor]];
[self.tableView setDataSource:self];
[self.tableView setDelegate:self];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.view addSubview:self.tableView];
[self.tableView setTableHeaderView:header];
Use the search bar delegate:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
self.searchDisplayController.searchResultsTableView.backgroundColor = [UIColor grayColor];
}
UISearchBarDelegate Protocol Reference
Use one of the delegate's methods:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
UISearchBar has property text so you can check for user input.
For example:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if([searchText length] > 0)
{
self.tableView.backgroundColor = [UIColor whiteColor];
}
else
{
self.tableView.backgroundColor = [UIColor clearColor];
}
}
I think You should do your changes in UISearchBarDelegate's method - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
Try to change colors in delegate function of search bar like
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar
(void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText
and put the self.tableView.backgroundColor = [UIColor yourcolor];

Issues with UISplitViewController and modal UIViewControllers

I have a really strange UI glitch in my iPhone/iPad app. Because I wanted to find the cause of it, I created a new project with as little code as possible. The important code is below.
Basically, I have a UISplitViewController containing two UIViewController subclasses. When a button is tapped, the first one presents modally and as UIModalPresentationFormSheet a UIViewController subclass called Modal. In there, when a button is tapped, another UIViewController subclass called Text is presented, this time as UIModalPresentationFullScreen. In Text, there is a UITextView. When it is tapped, everything is alright, but when the iPad is rotated, I get this:
The white part is the Text view controller, the red part in the background is ViewControllerTwo.
Does anybody have any idea why this happens? And what I can do to fix it?
Here is the Project: MediaFire
Here is the relevant source code:
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewControllerOne *one = [[ViewControllerOne alloc] init];
ViewControllerTwo *two = [[ViewControllerTwo alloc] init];
UISplitViewController *split = [[UISplitViewController alloc] init];
split.viewControllers = [NSArray arrayWithObjects:one, two, nil];
self.window.rootViewController = split;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
// ViewControllerOne.m
#import "Modal.h"
#implementation ViewControllerOne
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(30, 30, 44, 44);
[button addTarget:self action:#selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)buttonTapped
{
Modal *modalOne = [[Modal alloc] init];
modalOne.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:modalOne animated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
// Modal.m
#import "Text.h"
#implementation Modal
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *buttonOne = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonOne.frame = CGRectMake(30, 30, 44, 44);
buttonOne.tag = 1;
[buttonOne addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
UIButton *buttonTwo = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonTwo.frame = CGRectMake(30, 100, 44, 44);
buttonTwo.tag = 2;
[buttonTwo addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonOne];
[self.view addSubview:buttonTwo];
}
- (void)buttonTapped:(UIButton *)button
{
if (button.tag == 1)
{
Text *text = [[Text alloc] init];
text.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentModalViewController:text animated:YES];
}
else
{
[self dismissModalViewControllerAnimated:YES];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
// Text.m
#implementation Text
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
UITextView *textView = [[UITextView alloc] initWithFrame:self.view.bounds];
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:textView];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
You may need to present the modal views from the UISplitViewController instead of it's subviews...
UISplitViewController *splitViewController = [(AppDelegate *)[[UIApplication sharedApplication] delegate] splitViewController];
[splitViewController presentModalViewController:modal animated:YES];

Show activity indicator during application launch

I am trying to add an activity indicator during startup. I did have a launch image, but I'd rather have just an indicator alone. I added the following to my app delegate, but the indicator doesn't appear.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// *******Create activity indicator****
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(225, 115, 30, 30)];
[activity setBackgroundColor:[UIColor clearColor]];
[activity setActivityIndicatorViewStyle: UIActivityIndicatorViewStyleGray];
[window addSubview: activity];
[activity release];
[activity startAnimating];
activity.hidden = FALSE;
// *******End activity indicator****
MainViewController *viewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.mainViewController = viewController;
[window addSubview:[mainViewController view]];
mainViewController.view.frame = CGRectMake(0, 20, 320, 411);
[window addSubview:[rootController view]];
[window makeKeyAndVisible];
#if !TARGET_IPHONE_SIMULATOR
[application registerForRemoteNotificationTypes:
UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
#endif
application.applicationIconBadgeNumber = 0;
// Hide indicator
[viewController release];
activity.hidden = TRUE;
[activity stopAnimating];
return YES;
}
I visited this question from back in 2009, but the solution didn't work for me.
iPhone-SDK:Activity Indicator during Startup?
For all those who needed this, and i know there were many...
(Made on Xcode version 4.2.1)
So...
In the AppDelegate.h add this:
#property (nonatomic, strong) UIImageView *splashView;
In the AppDelegate.m add this:
On the top of the page of cours #synthesize splashView;
And then:
- (void) splashFade
{
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, 320, 480)];
splashView.image = [UIImage imageNamed:#"Default.png"];
[_window addSubview:splashView];
[_window bringSubviewToFront:splashView];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationDelay:2.5];
[UIView setAnimationTransition:UIViewAnimationTransitionNone forView:_window cache:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(startupAnimationDone:finished:context:)];
splashView.alpha = 0.0;
[UIView commitAnimations];
//Create and add the Activity Indicator to splashView
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.alpha = 1.0;
activityIndicator.center = CGPointMake(160, 360);
activityIndicator.hidesWhenStopped = NO;
[splashView addSubview:activityIndicator];
[activityIndicator startAnimating];
}
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
[splashView removeFromSuperview];
}
The [UIView setAnimationDelay:2.5] is responsible for how long the splashView will be in front by the delay time you choose.
You can change the position of the indicator by changing the nubmers of x/y in:
activityIndicator.center = CGPointMake(160, 360);
At last, under the methode:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Just add this:
[self performSelector:#selector(splashFade) withObject:nil];
And there you go :)
Hope it helped.
Have a nice programming....
I don't think this is possible from looking at the docs for UIApplicationDelegate
The earliest your app is called is in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Which as the name suggests is when your app hasFinishedLaunching
The reason nothing happens when you show and hide the indicator is to do with threading. The user interface will only update at the end of your method where the net result of your changes will be that the indicator is hidden.
Another solution is to let the launchimage load and when your ViewController loads at the viewDidLoad, use overlay + indicator on top of your view with alpha below 1:
UIView *baseView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.view addSubview:baseView];
[baseView setBackgroundColor:[UIColor blackColor]];
baseView.userInteractionEnabled = NO;
baseView.alpha = 0.4;
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
indicator.center = self.view.center;
[self.view addSubview:indicator];
[indicator bringSubviewToFront:self.view];
[indicator startAnimating];
Now comes the fun part, use delegate to remove the indicator + overlay when all your background threads finish to load up and simple call a method via your delegate:
- (void) onFinishLoading{
[indicator stopAnimating];
[baseView removeFromSuperview];
}
In order to stopAnimation and removeFromSuperView you might want to make baseView and indicator property + #synthesize
This isn't possible (as #Paul.S is saying). However, you can simulate this by adding a (static, so it's not animating) Default.png-splash-screen to your project.
In my opinion, though, you should rather invest in a better performance. For example: do you link your app with unused frameworks (eg. QuartzCore, MapKit) or something.
Starting an app should take 2 seconds max. At this time, you don't really need to show your user an activity-indicator. If it does take longer, you should take a look at what makes it so slow, rather than trying to hide it.