Wednesday, June 1, 2011
UITables with Downloaded Images – Easy Asynchronous Code
an example of method swizzling by customizing an UINavigationBar
an example of method swizzling by customizing an UINavigationBar
-
/* * Some references: * http://www.cocoadev.com/index.pl?MethodSwizzling * http://samsoff.es/posts/customize-uikit-with-method-swizzling * http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocCategories.html#//apple_ref/doc/uid/TP30001163-CH20-SW1 * http://developer.apple.com/iphone/library/documentation/uikit/reference/UIKitFunctionReference/Reference/reference.html * http://developer.apple.com/mac/library/documentation/GraphicsImaging/Reference/CGContext/Reference/reference.html * http://developer.apple.com/mac/library/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html * */ /* UINavigationBar+Additions.h */ #import
@interface UINavigationBar (Additions) - (void)drawRectCustom:(CGRect)rect; @end /* UINavigationBar+Additions.m */ #import "UINavigationBar+Additions.h" @implementation UINavigationBar (Additions) // When the object is set up it calls drawRect, which then loads this method because of the method swizzling - (void)drawRectCustom:(CGRect)rect { // If the style of the bar is the default style, apply our custom visuals if (self.barStyle == UIBarStyleDefault) { // Create the drawing context CGContextRef ctx = UIGraphicsGetCurrentContext(); // Set the background color of the navbar [[UIColor blackColor] set]; CGRect fillRect = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height); CGContextFillRect(ctx, fillRect); // Create an instance of the image we want to draw UIImage * logo = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"path/to/logo" ofType:@"png"]]; // Get the absolute center points relative to the image and the screen NSNumber * centerX = [NSNumber numberWithFloat:(self.frame.size.width/2 - logo.size.width/2)]; NSNumber * centerY = [NSNumber numberWithFloat:(self.frame.size.height/2 - logo.size.height/2)]; // Draw the image [logo drawInRect:CGRectMake(centerX.floatValue, centerY.floatValue, logo.size.width, logo.size.height)]; // End execution of the method return; } // By this time drawRectCustom is actually referencing to drawRect [self drawRectCustom:rect]; } @end /* main.m */ #import #import
Saturday, May 28, 2011
几个常用的Objective-C开源类库
几个常用的Objective-C开源类库
1. json-frameworkA strict JSON parser/generator for Objective-C
2. GTMBase64 Google Toolbox for Mac
3. TouchXML xml解析
4. SFHFKeychainUtils 安全保存用户密码到keychain中
5. MBProgressHUD 很棒的一个加载等待特效框架
6. ASIHTTPRequest http等相关协议封装
7. EGORefreshTableHeaderView 下拉刷新代码
8. AsyncImageView 异步加载图片并缓存代码
9. 类似setting的竖立也分栏程序
Thursday, May 19, 2011
How To Choose The Best XML Parser for Your iPhone Project
How To Choose The Best XML Parser for Your iPhone Project
There are a lot of options when it comes to parsing XML on the iPhone. The iPhone SDK comes with two different libraries to choose from, and there are several popular third party libraries available such as TBXML, TouchXML, KissXML, TinyXML, and GDataXML. How is a developer to choose?
I have been recently taking a look at the various options out there, and ended up extending the XMLPerformance sample from Apple to try out each of the above libraries to learn how they worked and compare their performance. I thought I’d share what I’ve learned thus far to others who might be searching for the best XML library for their iPhone project.
In this article we’ll give a detailed comparison of the features and performance of the most popular iPhone libraries, explain how to choose between them, and give a sample project showing how to read XML data using each of the above libraries.
SAX vs. DOM
Before we begin, I wanted to make sure everyone is aware of the most important difference between XML parsers: whether the parser is a SAX or a DOM parser.
- A SAX parser is one where your code is notified as the parser walks through the XML tree, and you are responsible for keeping track of state and constructing any objects you might want to keep track of the data as the parser marches through.
- A A DOM parser reads the entire document and builds up an in-memory representation that you can query for different elements. Often, you can even construct XPath queries to pull out particular pieces.
Ok, now let’s discuss some of the libraries!
The Most Popular XML Parsers for the iPhone
In my research, here’s what seemed to me to be the most popular XML Parsers for the iPhone, and a brief description of each one:
- NSXMLParser is a SAX parser included by default with the iPhone SDK. It’s written in Objective-C and is quite straightforward to use, but perhaps not quite as easy as the DOM model.
- libxml2 is an Open Source library that is included by default with the iPhone SDK. It is a C-based API, so is a bit more work to use than NSXML. The library supports both DOM and SAX processing. The libxml2 SAX processor is especially cool, as it has a unique feature of being able to parse the data as it’s being read. For example, you could be reading a large XML document from the network and displaying data that you’re reading for it to the user while you’re still downloading.
- TBXML is a lightweight DOM XML parser designed to be as quick as possible while consuming few memory resources. It saves time by not performing validation, not supporting XPath, and by being read-only – i.e. you can read XML with it, but you can’t then modify the XML and write it back out again.
- TouchXML is an NSXML style DOM XML parser for the iPhone. Like TBXML, it is also read-only, but unlike TBXML it does support XPath.
- KissXML is another NSSXML style DOM XML parser for the iPhone, actually based on TouchXML. The main difference is KissXML also supports editing and writing XML as well as reading.
- TinyXML is a small C-based DOM XML parser that consists of just four C files and two headers. It supports both reading and writing XML documents, but it does not support XPath on its own. However, you can use a related library – TinyXPath – for that.
- GDataXML is yet another NSXML style DOM XML parser for the iPhone, developed by Google as part of their Objective-C client library. Consisting of just a M file and a header, it supports both reading and writing XML documents and XPath queries.
Ok, now let’s start comparing all these libraries!
XML Parser Performance Comparison App
Apple has made an excellent code sample called XMLPerformance that allows you to compare the time it takes to parse a ~900KB XML document containing the top 300 iTunes songs with both the NSXML and libxml2 APIs.
The sample allows you to choose a parsing method and then parse the document, and it keeps statistics on how long it took to download the file and parse the file in a database. You can then go to a statistics screen to see the average download and parse times for each method.
I thought this would be an ideal way to test out how these various APIs performed against each other, so I extended the sample to include all of the above libraries. You can download the updated project below if you want to try it out on your device. It also serves as a nice example of how to use each of the above APIs!
Download Updated XMLPerformance Project
A note on the project: if the library included XPath support, I used it for a single lookup, because I felt it represented the way the library would be used in practice. But of course XPath is generally slower than manually walking through the tree, so it adds to the benchmarks for those libraries.
So anyway – I’ll discuss the results of how things performed on my device here with the sample written as-is – but feel free to give it a shot on your device, or tweak the code based on the actual XML data you need to parse!
XML Parser Performance Comparison
Here’s some graphs that shows how quickly the various parsers parsed the XML document on my device (an iPhone 3Gs):
As you can see here, NSXMLParser was the slowest method by far. TBXML was the fastest, which makes sense because many features were taken out in order to optimize parse time for reading only.
I was surprised, however, to see that TBXML and some of the other DOM parsing methods performed faster than libxml2′s SAX parser, which I had thought would be the fastest of all of the methods. I haven’t profiled it, but my guess as to why it is slower is because of the frequent string compares needed to parse the document in the SAX method.
However, don’t discount libxml2′s SAX method by looking at this chart. Remember that libxml2 is the only one of these methods that can parse the document as it’s reading in – so it can let your app start displaying data right away rather than having to let the download finish first.
Ok, here’s a graph that shows the peak memory usage by parser (this was obtained through running the various methods through the Object Allocations tool):
Note that the DOM methods usually require more memory overhead than the SAX methods (with the exception of TBXML, which is indeed quite efficient). This is something to consider when you are dealing with especially large documents, given the memory constraints on an iPhone.
Also note that libxml2′s SAX method is the best option as far as peak memory usage is concerned (and I suspect it would scale better than the others as well).
Finally, let’s wrap up with a chart that summarizes the differences between the parsers and everything we’ve discussed above:
NSXML | libxml2 – SAX | TBXML | TouchXML | KissXML | TinyXML | GDataXML | libxml2 – DOM | |
---|---|---|---|---|---|---|---|---|
Included with SDK? | Yes | Yes | No | No | No | No | No | Yes |
Seconds to Parse | 1.87 | 1.19 | 0.68 | 1.1 | 1.37 | 1.27 | 1.07 | 0.84 |
Peak Memory Usage | 3.11 | 3.01 | 3.07 | 6.5 | 5.25 | 4.8 | 4.15 | 4.97 |
Parse While Downloading? | No | Yes | No | No | No | No | No | No |
Edit/Save XML? | No | No | No | No | Yes | Yes | Yes | Yes |
XPath Support? | No | No | No | Yes | Yes | Yes* | Yes | Yes |
C or Obj-C | Obj-C | C | Obj-C | Obj-C | Obj-C | C | Obj-C | C |
License | Apple | MIT | MIT | MIT | MIT | ZLib | Apache | MIT |
* = with TinyXPath
Which To Choose?
Which XML parser to choose really depends on what you want to do with the parser.
- If you just want to read small XML documents, performance doesn’t matter as much with small documents. You probably want to pick something with XPath support and something that is written in Objective-C to make your job easier. So I’d recommend either TouchXML, KissXML, or GDataXML for this case.
- If you want to both read and write small XML documents, again performance doesn’t matter as much as functionality and ease of use. You probably want to pick something with XPath support, written in Objective-C, with read/write capability. So I’d recommend KissXML or GDataXML for this case.
- If you want to read extremely large XML documents, performance is the critical issue here. You’ll want to consider libxml2 SAX, TBXML, or libxml DOM for this, depending on what your exact situation is.
What about the ones I didn’t mention?
- NSXML is a decent choice if you’re dealing with relatively small documents, and you don’t feel like adding a third party library to the SDK.
- TinyXML could be an OK choice for medium sized documents if you already have experience with the API and are comfortable with C as it ports quite easily over to the iPhone.
I took a look at two other XML libraries during the course of this investigation (VTD-XML and Objective-XML), but I couldn’t get them working. If someone else has had more luck with these, feel free to extend the sample project to include them!
Where To Go From Here?
If you’re looking for some help using one of these libraries, check out my post on How to Read and Write XML Documents with GDataXML.
And if anyone has any additional feedback about these libraries or tips that may help other developers, please chime in below!
Wednesday, May 18, 2011
Singletons, AppDelegates and top-level data.
Singletons, AppDelegates and top-level data.
If you require only a single instance of an object in your application, where should it go? How should you control and manage it? Here are some different approaches, their implementations, their strengths and their weaknesses.
A little about global variables
They should scare you
Global variables is a term that invokes a sense of dread in experienced programmers. They are feared because a program filled with global variables (when it should use scoped variables instead) is a program without structure; a totally unmanageable mess.
This post will be entirely about writing and using global variables.
They're essential
The reality is that applications need to have some global state; we must have global variables. A variable will need to be global if all of the following are true:
- No other object owns, manages or is otherwise responsible for it
- There is exactly one in the whole program
- It is not a constant (like a string or number)
If all of these are true, then you should use a global variable.
If you're wondering, variables that fall outside these rules should be (respectively):
- child variables of the object that manages them
- children of the object that manages the collection
- a
#define
or aconst
(constants are state for the compiler, not the program)
In Cocoa, they're not quite global
Actually, what I will be showing are the "in practice" global variables of a Cocoa program — in reality, none of the techniques I show you will be true global variables in the Standard C Language sense but these approaches have replaced true global variables in Cocoa programs in almost all cases.
I will be showing you top-level child objects (children of the application delegate) and singleton objects. Explaining why these are normally considered equivalent to globals:
- The application object is the first constructed object in the program from which all other hierarchically arranged objects are constructed, making it effectively the top-level scope in the program (like a global scope). The application delegate should be considered a basic extension of the application object itself (especially since you should never override the application class).
- A singleton is an object that can be allocated only once (and can't be deleted) — making it a single, global instance. While singletons are stored in a true global variable, they are never accessed that way in Objective-C (a class method is used to access them), providing a least some abstraction around the implementation.
AppDelegates and AppControllers
As every Cocoa programmer knows, an application delegate is created in the MainMenu.xib file:
or the MainWindow.xib file for the iPhoneSDK.
A globally accessed variable can be initialized in the applicationDidFinishLauching
delegate method:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { myGlobalObject = [[MyGlobalObject alloc] init]; }
and assuming myGlobalObject
has an associated getter method, you can now access it globally using:
[[NSApp delegate] myGlobalObject]
or on the iPhone:
[[UIApplication sharedApplication].delegate myGlobalObject]
Since the delegate
property returns an id
(instead of the actual class of your delegate), you can't use property notation for the myGlobalObject
itself unless the myGlobalObject
is a property and you wrap the delegate in parentheses and inside the parentheses cast the delegate to your application delegate's class, like this:
((MyAppDelegate *)[UIApplication sharedApplication].delegate).myGlobalObject
Although I have seen people declare the following macro in their delegate's header:
#define UIAppDelegate \ ((MyAppDelegate *)[UIApplication sharedApplication].delegate)
which then allows you to use:
UIAppDelegate.myGlobalObject
from anywhere that includes your delegate's header.
Why AppDelegates are bad
Having explained that the above could be done, I will tell you that in my programs, I avoid using the AppDelegate
for anything other than:
- implemenations of the
NSApplication
delegate methods (includingapplicationDidFinishLaunching:
to finalize application construction) - handling menu items for items that don't exist in a window (for example, opening the application Preferences window)
Relying on your AppDelegate
object to manage your global variables can quickly get scary for the same reason that global variables in general are considered scary: you can easily put too much into this top level and it becomes a big, unstructured mess. This problem is an anti-pattern, often called the Big Ball of Mud.
It is a bad idea structurally for two reasons. The first is encapsulation. The AppDelegate should only really be connected to things related to the AppDelegate (i.e. the NSApplication
object and related state). If you store data related to other objects inside the AppDelegate, you rob those other objects of control over themselves.
Separation of concerns is the other serious problem. It is not the application delegate's responsibility to be the gatekeeper and manager of non-Application-related variables within the program.
A well-designed program organizes its classes so that they are all discrete, self-contained entities, shoving connections to everything into the AppDelegate
violates this idea.
Singletons in Cocoa
The solution to the encapsulation problem is to create classes that manage any global data as discreet modules. This is done through a singleton.
The basic recipe for making a singleton is given by Apple: Creating a Singleton Instance.
I personally like to put these singleton methods into a macro, which you can download in my SynthesizeSingleton.h file. If you #import
this header at the top of a class implementation, then all you need to do is write:
SYNTHESIZE_SINGLETON_FOR_CLASS(MyClassName);
inside the @implementation MyClassName
declaration and your class will become a singleton. You will also need to add the line:
+ (MyClassName *)sharedMyClassName;
to the header file for MyClassName
so the singleton accessor method can be found from other source files if they #import
the header.
Once your class is a singleton, you can access the instance of it using the line:
[MyClassName sharedMyClassName];
Note: A singleton does not need to be explicitly allocated or initialized (thealloc
andinit
methods will be called automatically on first access) but you can still implement the defaultinit
method if you want to perform initialization.
Advantages of a singleton
A well-designed singleton is a discrete, self-managing object that manages a specific role within your program.
Where variables hung off the Application delegate may have nothing in common with the delegate object itself, a singleton should be entirely focussed on its own specific role and responsibilities.
Search the Mac OS X 10.5 Reference in XCode for methods that begin with "shared" to see the ways in which Apple use singletons to create "manager" objects which allow you to get, set and manipulate entities that exist only once in a program.
Also, since the singleton is accessed through a method, there is some abstraction around the specific implementation — you could move from a true singleton to a per-thread based implementation if you needed, without changing the external interface.
Conclusion
Don't use globals unless you need to do so. An overwhelming majority of the data in your program has a sensible parent that is not the top-level. Singletons and top-level data should be used only when the data they contain truly belongs at the top level.
Cocoa singletons are flexible and useful when you need them. Using your application delegate to hold top-level data will work but try to limit its responsibilities to MainMenu.xib allocated objects where possible.
Monday, May 16, 2011
Posts Tagged UIActivityIndicatorView
Posts Tagged UIActivityIndicatorView
Apple iPhone Web Kit with Activity Indicator
Posted by Chris Danielson in iPhone Development on December 4th, 2009
Welcome to the club of searching for an overly simple UIWebView a.k.a. WebKit example! In this example, I’ll show you simply how to hand code a quick UIWebView into your program as well as to add a UIActivityIndicatorView a.k.a. an activity indicator. Without jabbing Apple too hard here, the documentation is pretty bad and that is why it’s nice to have an example just shown to you as-is. I hope this example helps shine a light on the situation for anyone wanting to implement a nice and quick Apple iPhone WebKit solution.
#import |
/* Inside the @implementation FirstViewController ... */ - (void)viewDidLoad { //We have a NIB file in play here, so I dropped the loadView here. Just make sure that your loadView is not getting called twice! [super viewDidLoad]; [self loadView]; } - (void)loadView { UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; self.view = contentView; CGRect webFrame = [[UIScreen mainScreen] applicationFrame]; webFrame.origin.y = 0.0f; myWebView = [[UIWebView alloc] initWithFrame:webFrame]; myWebView.backgroundColor = [UIColor blueColor]; myWebView.scalesPageToFit = YES; myWebView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); myWebView.delegate = self; [self.view addSubview: myWebView]; [myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.maxpowersoft.com/"]]]; activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0); activityIndicator.center = self.view.center; [self.view addSubview: activityIndicator]; } - (void)dealloc { [activityIndicator release]; [myWebView release]; [super dealloc]; } #pragma mark WEBVIEW Methods - (void)webViewDidStartLoad:(UIWebView *)webView { // starting the load, show the activity indicator in the status bar [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; [activityIndicator startAnimating]; } - (void)webViewDidFinishLoad:(UIWebView *)webView { // finished loading, hide the activity indicator in the status bar [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; [activityIndicator stopAnimating]; } - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { // load error, hide the activity indicator in the status bar [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; // report the error inside the webview NSString* errorString = [NSString stringWithFormat: @" |
That is all there is to it. It’s really simple as you can see. Feel free to copy and paste accordingly.
iPhone Dev Sessions: Making a Splash Screen
iPhone Dev Sessions: Making a Splash Screen
All too often an iPhone application’s launch sequence is an overlooked detail. The most common approach is to misuse the provided Default.png file as a splash screen. As it turns out, this detailing of an application is more than a little challenging if you want to get it right and stay within Apple’s guidelines.
The key to a smooth and professional looking launch sequence starts with knowing exactly where the application will land at startup. Some applications start at exactly the same place each and every successive launch, others attempt to preserve the application’s state and launch into the screen where the user last used the application. Keeping this in mind can change the strategy of how the launch sequence is implemented. This includes screen orientation as well as how and even if the status bar it to be displayed.
One may witness flickering of the status bar from blue, to black or from black to blue during the launch sequence. This is mainly due to the fact that there are two places to change the behavior of the status bar. One is hidden in the info.plist file, and the other is typically via code in the Application Delegate’s applicationDidFinishLaunching
method. The info.plist
configuration is used before the main window is loaded, and the code in the Application Delegate is used during the launching of the main window. The reason one may want to utilize both styles is to take advantage of a full screen splash page, and then enable the appropriate looking status bar once the application has finished loading.
For the purpose of this example application, we will assume that the user state is preserved between executions, and we do not know exactly what the screen will look like when the user enters the application. We will therefore be implementing a full-screen splash view that will have the status bar hidden during the launch sequence. Once the splash view has disappeared, a black opaque status bar will be utilized throughout the application. It is also assumed that the application will launch in portrait mode, and that the first screen the user will see will also be in portrait mode.
Editing the Configuration File
The first order of business is to take care of the status bar. In Xcode, locate the info.plist
file for the project. To add an additional property to the plist file, simply select one of the entries and click on the plus tab that appears to the right and select Status Bar Style from the drop down list:

Edit Projects plist File
There are only three different styles to choose from. Try each style out to see which one fits the needs of the application being developed. For this example we will set the style to UIStatusBarStyleDefault
.
UIStatusBarStyles:UIStatusBarStyleDefault
— Gray (the default)UIStatusBarStyleBlackTranslucent
— Transparent black (specifically, black with an alpha of 0.5)UIStatusBarStyleBlackOpaque
— Opaque black
If on the other hand the desire is to hide the status bar when the application launches, then yet another property needs to be set. In this case, add the “Status Bar is initially hidden” property to the plist file and be sure to check the box next to the property.
Editing the Application Delegate code
So now that the status bar style is set, and initially hidden, how does one get the status bar to display again? You can actually turn the status bar on and off programmatically via code. This is particularly handy when the need arises to display a full screen view, such as the splash screen this application is utilizing. In the applicationDidFinishLaunching
method of the Application’s designated AppDelegate class, add the following line of code to make the status bar visible again:
1 | - ( void )applicationDidFinishLaunching:(UIApplication *)application { |
2 | // Override point for customization after app launch |
3 | [window addSubview:viewController.view]; |
4 | [window makeKeyAndVisible]; |
5 | [[UIApplication sharedApplication] setStatusBarHidden: NO animated: YES ]; |
6 | } |
Adding a Default.png Image
Surprisingly, the size of this file is not as important as the naming convention of the file. Default.png is a case sensitive PNG file. The image should be 480×320 according to Apple. Following Apple’s conventions, this image should look like the view that the user will see when the application has launched, and not the actual splash screen.
Xcode provides a mechanism to create a Default.png file from an attached device running the application. From the Organizer window, select the device, click on screenshots and click capture. To make that screenshot your application’s default image, click Save As Default Image. Even though the image that is created includes the status bar as it looked when the screen shot was captured, the iPhone OS replaces it with the current status bar when your application launches. Just to be clear, this is not a splash screen…not yet.
Long Launch Sequences to Varying Views
So far, this is what most applications will implement if they implement any sort of controlled visual experience when the application launches. If you follow Apple’s guidelines, and the image you produce is the first screen that the user will see, all is good. Except, what if the launch sequence is not as fast as the user expects? What if the application preserves state and lands on a different view based on the users last know state? Then this technique is not up to the task.
Photoshop a branded image representing the application and save it as a PNG image sized at 480×320. Do not include a status bar of any kind in the image file being created. Add this image file to the project. Now the application sort of has a splash screen, through a misused implementation of the Default.png file. To correct this, simply add an image view as a property to the App Delegates header and create it as follows:
1 | splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; |
2 | splashView.image = [UIImage imageNamed: @"Default.png" ]; |
3 | [window addSubview:splashView]; |
4 | [window bringSubviewToFront:splashView]; |
At this point, the image view is utilizing the exact same image file that was created in Photoshop. There’s no chance of the initial view being different than the Default.png file at this point. The one remaining problem is the timing of when to remove the image view from the subview. This can be handled in one of two ways…
Controlling the Duration of the Splash Screen
The first option is for those with quick startup times that just want a splash screen. In this situation, create a method to remove and release the splash view, then calling that method via a timed perform selector call as follows:
1 | [ self performSelector: @selector (removeSplash) withObject: nil afterDelay:1.5]; |
The removeSplash
method does just that, removes the image view from the subview and releases the object.
1 | -( void )removeSplash; |
2 | { |
3 | [splashView removeFromSuperview]; |
4 | [splashView release]; |
5 | } |
The second method uses the same remove splash method, but relies on the built in event management to trigger when the method gets called.
1 | [[ NSNotificationCenter defaultCenter] addObserver: self |
2 | selector: @selector (saveClaim:) |
3 | name: @"RemoveSplashScreen" |
4 | object: nil ]; |
Now all that needs to be done is to post the notification from anywhere. This technique is particularly useful if the reason that the launch sequence is taking a long time has nothing to do with code that was implemented in the App Delegate.
1 | [[ NSNotificationCenter defaultCenter] |
2 | postNotificationName: @"RemoveSplashScreen" |
3 | object: nil ]; |
This technique can be employed from anywhere within the application. Removing the observer after the fact may avoid crashes if there is an opportunity for this notification to be fired multiple times. Releasing an object when no object it there to be released can lead to troublesome crashes to track down. The quick and dirty is to use the delay on the performSelector
call.
Conclusion
And there it is, a splash screen that conforms to Apple’s guidelines. No hidden APIs, no hacks, no special sauce. A simple, straight forward approach to making the initial interaction with the user as pleasant as possible.
References: