Super slow lag/delay on initial keyboard animation of UITextField - objective-c

Alright, this problem has been driving me nuts.
It takes roughly 3-4 seconds for the keyboard to pop up after I touch my UITextField. This only occurs on the first time the keyboard pops up since the app launched, afterwards the animation starts instantly.
At first I thought it was problem of loading too many images, or my UITableView, but I just created a brand new project with only a UITextField, and I still experience this problem. I'm using iOS 5, Xcode ver 4.2, and running on an iPhone 4S.
This is my code:
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(20, 20, 280, 30)];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.delegate = self;
[self.view addSubview:textField];
}
#end
Is this a common problem for all apps?
Right now, the only way I can make it somewhat better is by having textField become/resign first responder in viewDidAppear, but that doesn't solve the problem entirely - it just loads the delay onto when the view loads instead. If I click on textField immediately when the view loads, I still get the problem; if I wait 3-4 seconds after the view loads before touching the textField, I don't get the delay.

Before you implement any exotic hacks to get around this problem, try this: stop the debug session, close the app from multitasking, unplug your device from the computer and run the app normally by tapping its icon. I have seen at least two cases in which the delay only occurs while the device is plugged in.

So the problem is NOT just limited to the first install as I had previously thought, but happens every time the app is launched. Here's my solution that solves the issue completely.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Preloads keyboard so there's no lag on initial keyboard appearance.
UITextField *lagFreeField = [[UITextField alloc] init];
[self.window addSubview:lagFreeField];
[lagFreeField becomeFirstResponder];
[lagFreeField resignFirstResponder];
[lagFreeField removeFromSuperview];
}

Yeah, I also got a few seconds delay on the latest iPhone 4s. Don't panic. For some reasons, it only happens the first time the app is loaded from Xcode in Debug. When I did Release, I don't get the delay. Just forget it...

This is a known issue.
Preloading keyboard seems promising. Check Preloading the UIKeyboard.
Some additional reading material:
Initial iPhone virtual keyboard display is slow for a UITextField. Is this hack around required?
UITextField keyboard blocks runloop while loading?
http://www.iphonedevsdk.com/forum/iphone-sdk-development/12114-uitextfield-loooong-delay-when-first-tapped.html

You can use Vadoff's solution in Swift by adding this to didFinishLaunchingWithOptions:
// Preloads keyboard so there's no lag on initial keyboard appearance.
let lagFreeField: UITextField = UITextField()
self.window?.addSubview(lagFreeField)
lagFreeField.becomeFirstResponder()
lagFreeField.resignFirstResponder()
lagFreeField.removeFromSuperview()
It is working for me in iOS 8.

Code in block added to main queue and run asynchronously. (don't locked main thread)
dispatch_async(dispatch_get_main_queue(), ^(void){
[textField becomeFirstResponder];
});

See this answer. They suggest UIResponder+KeyboardCache. It's simple and awesome. Tested on iOS 7.

A related problem, where a UIViewController would be slow to present, was solved by using the system font instead of a custom font on the UITextField. Perhaps using the system font might also work for this problem?

This bug seems to be fixed in iOS 9.2.1. Since upgrading my device, I no longer have a delay between tapping a text field and the keyboard appearing when my device is connected to my computer.

This selected answer causes BAD_EXC crash on iOS 11 - remove from app to fix

You can add below code when viewController's view did loaded, like viewDidAppear.Not just application:didFinishLaunchingWithOptions:
UITextField *lagFreeField = [[UITextField alloc] init];
[self.window addSubview:lagFreeField];
[lagFreeField becomeFirstResponder];
[lagFreeField resignFirstResponder];
[lagFreeField removeFromSuperview];

Related

ios8: input accessory view crashed

I have an UITextView which have an accessory view.
Crashed Code:
textView.inputAccessoryView = _myView; // Code written in ViewDidLoad of my Controller
[textView becomeFirstResponder]; // <- Crash Point - Code called on button click
It had run perfectly before I updated my device to IOS 8.1. Now app crashes on the line
[textView becomeFirstResponder];
No Crash:
But if I comment the code like this, app does not crash.
//textView.inputAccessoryView = _myView;
[textView becomeFirstResponder];
I even tried to set the accessory View of textView to nil and then added mine but still app crashes. Although I have seen many questions where people have been able to successfuly add their accessory view to textView. I just can't figure out my issue.
I want to know the reason and solution for this.
Kindly look into this.

Troubles with UISearchBar \ UISearchDisplayViewController

I'm having a hard time with my SearchDisplayViewController on iOS 7.
I have a searchBar hidden over a UITableViewController, like
self.tableView.tableHeaderView = searchBar;
Problem is that when I tap on the searchBar to type in something, then the view starts greying out, and I quickly tap the screen in a random point to dismiss it, coming back to the tableView, the searchBar disappears. Totally. Only on iOS 7 though.
Debugging it, the frame is always the same: 0,0,320,44. But the bar is invisible!
Also tried to do
self.tableView.contentOffset = CGPointMake(0,self.searchDisplayController.searchBar.frame.size.height);
still disappears when I do it quickly.
On iOS 6 it works just fine. Problem is only with iOS 7 as far as I'm seeing.
I don't know what it depends on, has anyone encountered the same problem I have?
As of Double tap UISearchBar with search delegate on iOS 7 causes UISearchBar to disappear, I found the workaround to actually work and solved the bug - for now.
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
I encountered the same issue, and noticed that searchDisplayControllerDidEndSearch was being called twice. The first time, the superview of self.searchDisplayController.searchBar is the UITableView, and the second time it's still a UIView.
With the accepted answer, I worry about unintended consequences or unneeded overhead from re-inserting the subview every time the search bar is double-tapped, and I also worry about it breaking with future iOS versions. Fortunately, we can take advantage of the superview strangeness like this:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
if (self.tableView != self.searchDisplayController.searchBar.superview) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
If I had to guess what was happening, the UISearchBar is automatically creating a temporary UIView as its superview when it's active – this is the view seen when the search is being performed. While the UISearchBar is being dismissed, the superview gets set back to be the UITableView it had before, unless it gets dismissed so quickly that it was never properly initialized, in which case it cleans up improperly and the UITableView never gets the UISearchBar back as its child.
This solution still isn't ideal, and I think Apple must be doing something different in its own apps because their search bar UX feels a bit better. I think it would be better not to handle the second tap in the first place until the UISearchBar was ready. I tried using the other UISearchBarDelegate methods to do this, but I couldn't find an appropriate hook to override the current behavior.
I had the same problem with iOS 7 and I solved it from the apple documentation. The error most people do is that they associate the UISearchBar variable to the self.searchDisplayController.searchBar as the same...! NO NO..! They are 2 different things!!! UISearchBar should be declared and initialized and then wrapped into self.searchDisplayController as searchBar then later wrapped into self.tableView.tableHeaderView by so doing it will not disappear!!!
self.searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchDisplayController.delegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.searchResultsDelegate = self;
[self.searchBar setPlaceholder:#"search the hell in me"];
self.tableView.tableHeaderView = self.searchDisplayController.searchBar;
More refined approach for #lehrblogger solution:
- (void)addSearchDisplayControllerBackToTableView {
if ([self.searchDisplayController.searchBar isDescendantOfView:self.tableView] == NO) {
NSLog(#"Search bar is not in current table view, will add it back");
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
[self.searchDisplayController setActive:NO animated:YES];
}
}
Reason for this approach: While searching the search bar is moved to search container and the superview of search bar is always some other view other than current table view.
Note: This will dismiss the search, because user tapped more than once on search bar.

UITextView editable on iOS5 but not iOS6

I have a few UITextViews in my App. Since I have started building from XCode 4.5 the UITextView is sort of not editable in iOS6 but built on an iOS5 device they are fully editable.
The code hasnt changed sinced I started using iOS6. The XIB has been around since iOS4
Symptoms are...
iOS6
Can raise keyboard with tap and delete characters , but cant type anything else
Cant lower keyboard with keyboard hide key.
iOS5
Can raise keyboard with tap and edit fully.
Cant lower keyboard with keyboard hide key.
Im mystified. The textViewDidBeginEditing: gets hit so I know its wired up correctly.
The test project approach didnt work as I couldnt reproduce the issue.
In the end the fix was this which wasnt present.
[window makeKeyAndVisible];
as in...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.viewController = [[[MyViewController alloc] initWithNibName:nil bundle:nil] autorelease];
self.navigationController = [[[UINavigationController alloc] initWithRootViewController:self.viewController] autorelease];
[window setRootViewController:navigationController];
[window makeKeyAndVisible];
}
In my case this happened because the UIAltertView was called twice.
You might not think you never typed any keys, but the BackSpace key does qualify.
Then I removed the second UIAlertView and the TextField worked.
I think you need to check the UIAlertView code.

Cocoa: NSPanel loses parent and other strange behaviour

I have an issue with an NSPanel acting strangely and have created a Sample App to demonstrate this behaviour.
The App was generated from Xcode 4's template and simply creates a panel and then opens and closes it based on button presses:
The strange behaviour I have observed:
Under Lion, after opening the panel for the first time, the panel follows the main window around, which is correct behaviour. However after closing it and then re-opening it no longer follows the main window around.
Under Snow Leopard, when closing the panel the main window is also closed!
EDIT: Just to be clear; the behaviour I expect is for the panel to follow the main window around when the main window is moved; and for that to be true after the panel is closed and subsequently re-opened. Also I expected the panel and main window to behave the same way under Snow Leopard and Lion.
The important part of the code is here:
#implementation MyAppDelegate
- (void)dealloc
{
[_panel release];
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_panel = [[NSPanel alloc]
initWithContentRect:NSMakeRect(400, 400, 200, 100)
styleMask:NSUtilityWindowMask|NSClosableWindowMask|NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:YES];
[_panel setTitle:#"A Panel"];
}
- (IBAction)openPanel:(id)sender
{
[_panel setParentWindow:[self window]];
[_panel makeKeyAndOrderFront:sender];
}
- (IBAction)closePanel:(id)sender
{
[_panel orderOut:sender];
}
#end
Note: I added the unnecessary setParentWindow call to the openPanel method to assert that the parent window is reset every time the panel was opened; it made no difference however.
Can someone please explain what I'm doing wrong?
EDIT: There is some confusion about the missing #synthesize window = _window from the implementation file, but I have just opened another project sample that I created to test for memory leak behaviour and it's not in there either. I am using Xcode 4.4, so it's possibly a bug in that, however I don't think the project templates have changed.
You're not supposed to set the parent-child relationship from the child, but from the parent. The setParentWindow: docs say:
This method should be called from a subclass when it is overridden by a subclass’s implementation. It should not be called otherwise.
Instead, use addChildWindow:ordered:, like so:
- (IBAction)openPanel:(id)sender
{
[[self window] addChildWindow:_panel ordered:NSWindowAbove];
//[_panel setParentWindow:[self window]];
[_panel makeKeyAndOrderFront:sender];
}
I didn't test this under Snow Leopard, but it fixes the behavior for me when run on Lion.
Rob Keniger notes below that on Snow Leopard you should also do [[self window] removeChildWindow:_panel] before ordering the panel out. (I assume this is also a good idea on Lion.)
I'm a little confused about your question. I downloaded and checked out your project. First, somehow you're missing the #synthesize command for the application's main window. You said you used the template but somehow it's missing. Since the AppDelegate header file has a #property for the window, you need an #synthesize command in the implementation file for it. I'm not sure how you lost that line in your project but add this just after the #implementation line...
#synthesize window = _window;
Second, why do you state the following?
Under Lion, after opening the panel for the first time, the panel
follows the main window around, which is correct behavior.
What you're saying makes no sense. When you create your panel you use "initWithContentRect:NSMakeRect(400, 400, 200, 100)". Notice that you create it at position (400, 400) with a size of (200, 100). So the first time it opens it opens at the screen location (400, 400). It has nothing to do with the position of the main window.
Anyway, after adding the #synthesize part I could compile and run the application with no errors. It's working as expected in 10.7 for me.

How can I display a splash screen for longer on an iPhone?

How can I display a splash screen for a longer period of time than the default time on an iPhone?
Read the Apple iPhone Human Interface Guidelines (HIG). The "splash screen" isn't supposed to be for branding or displaying a logo, it's supposed to look like the default condition of the app so it appears to start up quickly.
Making it stay there for longer would be a violation of the HIG.
The simplest way to do this is to create a UIImageView who's image is your Default.png. In your applicationDidFinishLaunching: method, add that image view to your window, and hide it when you'd like your splash screen to go away.
I needed to do this to block showing a table view until the data was loaded over the network. I used a variation of one I found here:
http://michael.burford.net/2008/11/fading-defaultpng-when-iphone-app.html
In the interface of your App Delegate:
#interface AppDelegate : NSObject
{
UIImageView *splashView;
}
In the implementation:
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// After this line: [window addSubview:tabBarController.view];
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
splashView.image = [UIImage imageNamed:#"Default.png"];
[window addSubview:splashView];
[window bringSubviewToFront:splashView];
// Do your time consuming setup
[splashView removeFromSuperview];
[splashView release];
}
Make sure you have a Default.png in the resources
in your appDelegate , theres a method called applicationDidFinishedLaunching use a sleep function. Pass a digit in the sleep function for the no. of seconds you want to hold screen.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[window makeKeyAndVisible];
[window addSubview:viewController.view];
sleep(5);
return YES;
}
I searched so much for this thing and everybody gave their own complex point of view. I couldn't find a simple way that would just let me do it.
KISS ( Keep it simple and Smart :)
I avoided the actual as its offensive.
Even though it is against the guidelines but if you still want to do this than a better approach rather than sleeping thread will be
//Extend the splash screen for 3 seconds.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
this way the main thread is not blocked and if it is listening for any notifications and some other network related stuff, it still carries on.
UPDATE FOR SWIFT:
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow:3))
I did it pretty simply, by having my rootViewController push a modalViewController, loading from "Splash.nib" in a subclass of UIViewController I called "SplashViewController". The exact call was:
- (void) viewDidLoad {
SplashViewController *splashScreen = [[[SplashViewController alloc]
initWithNibName:#"SplashViewController" bundle:nil] autorelease];
[self presentModalViewController:splashScreen animated:NO];
//continue loading while MVC is over top...
When you launch the app, it pops right up, like a splash screen should. Then, the SplashViewController nib is just a full-screen UIImageView with a splash png, 320x480. After a 1-second NSTimer (anything more did seem to get in the way), it fires timerFireMethod, a custom method that just calls
[self dismissModalViewControllerAnimated:YES];
Then the modal VC just slides down and away, leaving my top tableView. The nice thing is, while the MVC is up, the underlying table can continue to load due to the independent nature of modal view controllers. So, I don't think this violates the HIGs, and actually does allow for faster launching. What would you rather look at, a cute picture, or an empty default view (snore)?
Yes, the simplest way is (remember to add your 'default.png' to targets -> [yourProjectName]: launch images in 'xCode'):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[NSThread sleepForTimeInterval:3.0];
}
Make your app take longer to load.
In all seriousness, Paul Tomblin is correct that this usually isn't a good idea. Default.png is a mechanism intended to make your app appear to load faster by holding an "empty" screenshot. Using it for a splash screen is a minor abuse, but intentionally making that splash screen appear for longer than it needs to is almost sick. (It will also degrade your user experience. Remember, every second the splash screen is visible is a second that the user is impatiently staring at your logo, swearing they'll switch to the first decent competitor they can find.)
If you're trying to cover for some sort of secondary loading--for example, if the interface has loaded and you're just waiting to get some data from the network--then it's probably okay, and Ben Gottlieb's approach is fine. I'd suggest adding a progress bar or spinner to make it clear to the user that something really is going on.
simply use sleep(time in seconds); in your applicationDidFinishedLaunching method
Here is my simple splash screen code. 'splashView' is an outlet for a view
that contains an image logo, UIActivityIndicator, and a "Load.." label (added
to my 'MainWIndow.xib' in IB). The activity indicator is set to 'animating' in IB,
I then spawn a separate thread to load the data. When done, I remove the
splashView and add my normal application view:
-(void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:splashView];
[NSThread detachNewThreadSelector:#selector(getInitialData:)
toTarget:self withObject:nil];
}
-(void)getInitialData:(id)obj {
[NSThread sleepForTimeInterval:3.0]; // simulate waiting for server response
[splashView removeFromSuperview];
[window addSubview:tabBarController.view];
}
Inside your AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Sleep is code to stop the slash screen for 5MoreSeconds
sleep(5);
[self initializeStoryBoardBasedOnScreenSize];
return YES;
}
//VKJ
In Xcode 6.3, you can show the launch screen.xib and even put a indicator on it, first it will show the default launch screen it is replaced by the nib so the user doesn't know it changed and then if everything is loaded hide it :-)
func showLaunchScreen() {
// show launchscreen
launchView = NSBundle.mainBundle().loadNibNamed("LaunchScreen", owner: self, options: nil)[0] as! UIView
launchView.frame = self.view.bounds;
self.view.addSubview(launchView)
// show indicator
launchScreenIndicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 50, 50)) as UIActivityIndicatorView
launchScreenIndicator.center = CGPointMake(self.view.center.x, self.view.center.y+100)
launchScreenIndicator.hidesWhenStopped = true
launchScreenIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
launchView.addSubview(launchScreenIndicator)
self.view.addSubview(launchView)
launchScreenIndicator.startAnimating()
self.navigationController?.setNavigationBarHidden(self.navigationController?.navigationBarHidden == false, animated: true) //or animated: false
}
func removeLaunchScreen() {
println("remove launchscreen")
self.launchView.removeFromSuperview()
self.launchScreenIndicator.stopAnimating()
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
Write an actual splash screen class.
Here's a freely usable splash screen that I recently posted in my iPhone in Action blog:
http://iphoneinaction.manning.com/iphone_in_action/2009/03/creating-a-splash-screen-part-one.html
The simplest way is to put your application's main thread into a sleep mode for desired period of time. Provided that "Default.png" exists in your application's bundle it will be displayed for as long as the main thread is asleep:
-(void)applicationDidFinishLaunching:(UIApplication *)application {
[NSThread sleepForTimeInterval:5];
window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[window setBackgroundColor:[UIColor yellowColor]];
[window makeKeyAndVisible];
}
As you are already aware, it's a horribly bad idea to do but it should work just fine...
just make the window sleep for some seconds in applicationDidFininshLaunchings method
example: sleep(3)
According to the Apple HIG you should not do that. But if your application needs to do so for definite purpose, you can do:
import <unistd.h> in your AppDelegate.m
Write the following line at the first of the "application didFinishLaunchingWithOptions:" method
sleep(//your time in sec goes here//);
I have done this as below:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"LaunchScreen" bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"splashView"];
self.window.rootViewController = viewController;
Note: LaunchScreen is the launch story borad and splashView is the storyboardIdentifier
For stylish splash screen tutorial check out this
http://adeem.me/blog/2009/06/22/creating-splash-screen-tutorial-for-iphone/
I agree it can make sense to have a splash screen when an app starts - especially if it needs to get some data from a web site first.
As far as following Apple HIG - take a look at the (MobileMe) iDisk app; until you register your member details the app shows a typical uitableview Default.png before very quickly showing a fullscreen view.
What I did is presented a modalview controller in the initial screen and then dissmiss it after several seconds
- (void)viewDidLoad
{
[super viewDidLoad];
....
saSplash = [storyboard instantiateViewControllerWithIdentifier:#"SASplashViewController"];
saSplash.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentModalViewController: saSplash animated:NO];
}
-(void) dismissSASplash {
[saSplash dismissModalViewControllerAnimated:NO];
}
There are many options already posted here, but I ran into cocoapod today that allows you to display the contents of your LaunchScreen.xib as the initial view controller:
https://github.com/granoff/LaunchScreen (Also see this blog post from the author with more implementation details.)
This seems like a fairly straight-forward way to do this, and better than the vast majority of answers posted here. (Of course it wasn't possible until the introduction of LaunchScreen files in the first place.) It is possible to display an activity indicator (or anything else you want) on top of the view.
As for why you would want to do this, I'm surprised that no one has mentioned that there are often publisher and/or partner requirements around this sort of thing. It's VERY common in games, but advertising-funded applications as well.
Also note that this does act counter to the HIG, but then so does waiting to load any content after your application launches. Remember that the HIG are guidelines, and not requirements.
One final note: It's my personal opinion, that any time an initial screen like this is implemented, you should be able to tap to dismiss it.
Swift 2.0
Use following line in didFinishLaunchingWithOptions: delegate method:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSThread.sleepForTimeInterval(5.0)
return true
}
But I recommend this:
Put your image in a UIImageView full screen as a subview on the top of your main view thus covering your other UI. Set a timer to remove it after some seconds (possibly with effects) now showing your application.
import UIKit
class ViewController: UIViewController
{
var splashScreen:UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
self.splashScreen = UIImageView(frame: self.view.frame)
self.splashScreen.image = UIImage(named: "Logo.png")
self.view.addSubview(self.splashScreen)
var removeSplashScreen = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: "removeSplashImage", userInfo: nil, repeats: false)
}
func removeSplashImage()
{
self.splashScreen.removeFromSuperview()
}
}
There is default image (default.png) is shown when you start your app.
So you can add a new viewcontroller which will display the that default image in the application didFinishLoading.
So by this logic you display the default image for a bit longer.
Swift version:
Add this line in the AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSThread.sleepForTimeInterval(2.0)//in seconds
return true
}