Best iphone questions in October 2011

iMessage Style Receding Keyboard in an iOS App

23 votes

I've been wondering if it is possible to replicate the behavior of Apple's iOS5 keyboard in the messages app, without using any private API calls. When you scroll down past the keyboard in the messages app, the keyboard will collapse leaving more room to see messages - try it to see.

I couldn't find anything that points towards making this without having to start jumping through some serious hoops to get an instance of the Keyboard's View. And I'm pretty sure Apple wouldn't be happy with that.

( in the mean time I have wrote my own implementation using the above method of jumping through the view hierarchy. https://github.com/orta/iMessage-Style-Receding-Keyboard )

This is an incomplete solution, however it should give you a good starting point.

Add the following ivars to your UIViewController:

CGRect        keyboardSuperFrame; // frame of keyboard when initially displayed
UIView      * keyboardSuperView;  // reference to keyboard view

Add an inputAccessoryView to your text controller. I created an small view to insert as the accessoryView:

accView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
accView.backgroundColor = [UIColor clearColor];
textField.inputAccessoryView = accView;

I added the above code to -(void)loadView

Register to receive UIKeyboardDidShowNotification and UIKeyboardDidHideNotification when view is loaded:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(keyboardWillShow:)
        name:UIKeyboardWillShowNotification
        object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(keyboardDidShow:)
        name:UIKeyboardDidShowNotification
        object:nil];
    return;
}

Add methods to specified as the selectors for the notifications:

// method is called whenever the keyboard is about to be displayed
- (void)keyboardWillShow:(NSNotification *)notification
{
    // makes keyboard view visible incase it was hidden
    keyboardSuperView.hidden = NO;
    return;
}
// method is called whenever the keyboard is displayed
- (void) keyboardDidShow:(NSNotification *)note
{
    // save reference to keyboard so we can easily determine
    // if it is currently displayed
    keyboardSuperView  = textField.inputAccessoryView.superview;

    // save current frame of keyboard so we can reference the original position later
    keyboardSuperFrame = textField.inputAccessoryView.superview.frame;
    return;
}

Add methods to track touched and update keyboard view:

// stops tracking touches to divider
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGRect newFrame;
    CGRect bounds = [[UIScreen mainScreen] bounds];

    newFrame = keyboardSuperFrame;
    newFrame.origin.y = bounds.size.height;  

    if ((keyboardSuperView.superview))
        if (keyboardSuperFrame.origin.y != keyboardSuperView.frame.origin.y)
            [UIView animateWithDuration:0.2
                    animations:^{keyboardSuperView.frame = newFrame;}
                    completion:^(BOOL finished){
                                keyboardSuperView.hidden = YES;
                                keyboardSuperView.frame = keyboardSuperFrame;
                                [textField resignFirstResponder]; }];
    return;
}


// updates divider view position based upon movement of touches
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
   UITouch  * touch;
   CGPoint    point;
   CGFloat    updateY;

   if ((touch = [touches anyObject]))
   {
      point   = [touch locationInView:self.view];
      if ((keyboardSuperView.superview))
      {
         updateY = keyboardSuperView.frame.origin.y;
         if (point.y < keyboardSuperFrame.origin.y)
            return;
         if ((point.y > updateY) || (point.y < updateY))
            updateY = point.y;
         if (keyboardSuperView.frame.origin.y != updateY)
            keyboardSuperView.frame = CGRectMake(keyboardSuperFrame.origin.x,
                                                 point.y,
                                                 keyboardSuperFrame.size.width,
                                                 keyboardSuperFrame.size.height);
      };
   };
   return;
}

Disclaimers:

  • When resigning as first responded, the keyboard moves back to its original position before sliding off screen. To make dismissing the keyboard more fluid, you first need to create an animation to move the keyboard off of the screen and then hide the view. I'll leave this part as an exercise to the readers.
  • I've only tested this on the iOS 5 simulator and with an iPhone with iOS 5. I have not tested this with earlier versions of iOS.

The SlidingKeyboard project I created to test this concept is available from GitHub in the examples directory of BindleKit:

https://github.com/bindle/BindleKit

Edit: Updating example to address first disclaimer.

Possible to target older iOS versions when using XCode 4.2 and iOS 5 SDK?

13 votes

I just recently downloaded the Xcode 4.2 with iOS 5 SDK package and I immediately noticed that I am unable to use my iPhone 3Gs with iOS 4.2.1 for debugging. I am only able to debug and test on my iphone 4 with iOS 5 installed.

For any of my devices running any iOS less than 5.0 Xcode just says "Finished running for iPhone3GS" and doesn't run it. I have my deployment target set to 3.0 because I am not using any features in my app above 3.0 and I want to be able to target all versions 3.0 or higher. I have all my provisioning profiles installed and up to date.

I have quit and restarted xcode, I have cleaned the build multiple times. I have tried adding armv6 in the archtecture build settings, but nothing will allow me to build and run my app on my old phone with ios 4.2.1.

My question is, is it possible to still make apps with the new SDK work for iOS versions lower than 5? How can I be sure that my app will still run on lower iOS versions if I am unable to install them on my old phone?

Edit: OK it looks like iOS SDK 5 dropped support for armv6 by default. What you have to do is add armv6 in the Architectures field under Build Settings for both your Target and your Project. Then your app will be able to run on iPhone 3G devices. Answer can be found here How to build for armv6 and armv7 architectures with iOS 5

Thanks, Greg

I was having the same issue trying to get a newly created Xcode 4.2 project running on an iPhone 3G 4.2.1. Here is how I was able to get it to run.

1) Change the Target's "Build Settings" ==> "Architecture" from "Standard (armv7)" to "other". Add armv6 and armv7.

2) Change the Target's "Build Settings" ==> "Valid Architecture" to armv6 and armv7.

3) Change the Target's "Build Settings" ==> "iOS Deployment Target" to iOS 4.2.

enter image description here

4) Open the projects *-Info.plist, remove the setting "Required device capabilities" (note it required armv7) enter image description here

btw I figured this out when I tried to manually add the app via the organizer and it reported:

Can't install application

The Info.plist for application at /Users/.../TestsDebug.app specifies device capability requirements, which are not met by Dev iPhone 3G

All should work after that. Talk about a PITA.

iCloud basics and code sample

11 votes

As a beginner, I'm struggling with iCloud. There are some samples, but they are usually quite detailed (on the developer forum there is one for iCloud and CoreData which is massive). The apple docs are OK, but I still can't see the big picture. So please bear with me, some of these questions are quite fundamental, but possibly easy to answer.

Context: I have a very simple iCloud app running (full sample code below). There is only one UITextView shown to the user and his/her input is saved in a file called text.txt.

enter image description here

The txt file is pushed to the cloud and made available to all devices. Works perfectly, but:

Main problem: What about users who do not use iCloud?

When I launch my app (see code below), I check if the user has iCloud enabled. If iCloud is enabled, everything is fine. The app goes ahead and looks for text.txt in the cloud. If found, it will load it and display it to the user. If text.txt is not found in the cloud, it will simply create a new text.txt and will display that to the user.

If the user does not have iCloud enabled, nothing will happen. How will I make it possible that non-iCloud users can still work with my text app? Or do I simply ignore them? Would I need to write separate functions for non-iCloud users? I.e. functions in which I simply load a text.txt from the documents folder?

Apple writes:

Treat files in iCloud the same way you treat all other files in your app sandbox.

However, in my case there is no 'normal' app sandbox anymore. It's in the cloud. Or do I always load my text.txt from disk first and then check with iCloud if there is something more up-to-date?

Related problem: File structure - Sandbox vs. Cloud

Perhaps my main problem is a fundamental misunderstanding of how iCloud is supposed to work. When I create a new instance of an UIDocument, I'll have to overwrite two methods. First - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError to get files from the cloud and then -(id)contentsForType:(NSString *)typeName error:(NSError **)outError to get files into the cloud.

Do I have to incorporate separate functions which will also save a local copy of text.txt into my sandbox? Will this work for non-iCloud users? As I understand iCloud, it will save a local copy of text.txt automatically. So there shouldn't be any need for me save anything into the 'old' sandbox of my app (i.e. as it used to be in the old, pre-iCloud days). Right now, my sandbox is totally empty, but I don't know if this is correct. Should I keep another copy of text.txt in there? This feels like cluttering my data structure... as there is one text.txt in the cloud, one in the iCloud sandbox on my device (which will work even if I am offline), and a third one in the good old sandbox of my app...


MY CODE: A simple iCloud sample code

This is loosely based on an example I found in the developer forum and on the WWDC session video. I stripped it down to the bare minimum. I'm not sure that my MVC structure is any good. The model is in the AppDelegate which isn't ideal. Any suggestions to make it better are welcome.


EDIT: I tried to extract the main question and posted it [here].4


OVERVIEW:

Overview

The most important bit which loads the text.txt from the cloud:

//  AppDelegate.h
//  iCloudText

#import <UIKit/UIKit.h>

@class ViewController;
@class MyTextDocument;

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    NSMetadataQuery *_query;
}

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController *viewController;
@property (strong, nonatomic) MyTextDocument *document;

@end

//  AppDelegate.m
//  iCloudText

#import "AppDelegate.h"
#import "MyTextDocument.h"
#import "ViewController.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize viewController = _viewController;
@synthesize document = _document;

- (void)dealloc
{
    [_window release];
    [_viewController release];
    [super dealloc];
}

- (void)loadData:(NSMetadataQuery *)query {

    // (4) iCloud: the heart of the load mechanism: if texts was found, open it and put it into _document; if not create it an then put it into _document

    if ([query resultCount] == 1) {
        // found the file in iCloud
        NSMetadataItem *item = [query resultAtIndex:0];
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:url];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc openWithCompletionHandler:^(BOOL success) {
            if (success) {
                NSLog(@"AppDelegate: existing document opened from iCloud");
            } else {
                NSLog(@"AppDelegate: existing document failed to open from iCloud");
            }
        }];
    } else {
        // Nothing in iCloud: create a container for file and give it URL
        NSLog(@"AppDelegate: ocument not found in iCloud.");

        NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
        NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"] URLByAppendingPathComponent:@"text.txt"];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:ubiquitousPackage];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc saveToURL:[doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
            NSLog(@"AppDelegate: new document save to iCloud");
            [doc openWithCompletionHandler:^(BOOL success) {
                NSLog(@"AppDelegate: new document opened from iCloud");
            }];
        }];
    }
}

- (void)queryDidFinishGathering:(NSNotification *)notification {

    // (3) if Query is finished, this will send the result (i.e. either it found our text.dat or it didn't) to the next function

    NSMetadataQuery *query = [notification object];
    [query disableUpdates];
    [query stopQuery];

    [self loadData:query];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
    _query = nil; // we're done with it
}

-(void)loadDocument {

    // (2) iCloud query: Looks if there exists a file called text.txt in the cloud

    NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
    _query = query;
    //SCOPE
    [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
    //PREDICATE
    NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, @"text.txt"];
    [query setPredicate:pred];
    //FINISHED?
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
    [query startQuery];

}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSLog(@"AppDelegate: app did finish launching");
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

    // Override point for customization after application launch.
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil] autorelease];
    } else {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil] autorelease];
    }

    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    // (1) iCloud: init

    NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    if (ubiq) {
        NSLog(@"AppDelegate: iCloud access!");
        [self loadDocument];
    } else {
        NSLog(@"AppDelegate: No iCloud access (either you are using simulator or, if you are on your phone, you should check settings");
    }


    return YES;
}

@end

The UIDocument

//  MyTextDocument.h
//  iCloudText

#import <Foundation/Foundation.h>
#import "ViewController.h"

@interface MyTextDocument : UIDocument {

    NSString *documentText;
    id delegate;

}

@property (nonatomic, retain) NSString *documentText;
@property (nonatomic, assign) id delegate;

@end

//  MyTextDocument.m
//  iCloudText

#import "MyTextDocument.h"
#import "ViewController.h"

@implementation MyTextDocument

@synthesize documentText = _text;
@synthesize delegate = _delegate;

// ** READING **

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
{
    NSLog(@"UIDocument: loadFromContents: state = %d, typeName=%@", self.documentState, typeName);

    if ([contents length] > 0) {
        self.documentText = [[NSString alloc] initWithBytes:[contents bytes] length:[contents length] encoding:NSUTF8StringEncoding];
    }
    else {
        self.documentText = @"";
    }

    NSLog(@"UIDocument: Loaded the following text from the cloud: %@", self.documentText);


    // update textView in delegate...
    if ([_delegate respondsToSelector:@selector(noteDocumentContentsUpdated:)]) {
        [_delegate noteDocumentContentsUpdated:self];
    }

    return YES;

}

// ** WRITING **

-(id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
    if ([self.documentText length] == 0) {
        self.documentText = @"New Note";
    }

    NSLog(@"UIDocument: Will save the following text in the cloud: %@", self.documentText);

    return [NSData dataWithBytes:[self.documentText UTF8String] length:[self.documentText length]];
}
@end

THE VIEWCONTROLLER

//
//  ViewController.h
//  iCloudText

#import <UIKit/UIKit.h>

@class MyTextDocument;

@interface ViewController : UIViewController <UITextViewDelegate> {

    IBOutlet UITextView *textView;

}

@property (nonatomic, retain) UITextView *textView;
@property (strong, nonatomic) MyTextDocument *document;

-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument;

@end

//  ViewController.m
//  iCloudText

#import "ViewController.h"
#import "MyTextDocument.h"

@implementation ViewController

@synthesize textView = _textView;
@synthesize document = _document;

-(IBAction)dismissKeyboard:(id)sender {

    [_textView resignFirstResponder];

}

-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument
{
    NSLog(@"VC: noteDocumentsUpdated");
    _textView.text = noteDocument.documentText;
}

-(void)textViewDidChange:(UITextView *)theTextView {

     NSLog(@"VC: textViewDidChange");
    _document.documentText = theTextView.text;
    [_document updateChangeCount:UIDocumentChangeDone];

}

I've been using your example and I like it for helping me grasp the basics of iCloud. Now I'm wrangling with your question for my own app which has to support existing users of the app with locally stored content who may or may not be using iCloud creating these cases as far as I can tell:

Cases:

  1. New user
    • has icloud - create documents in icloud
    • no icloud - create documents locally
  2. Existing user
    • has icloud
      • just added - migrate local docs to icloud
      • not just added - open/save docs to icloud
    • no icloud
      • just removed - migrate former icloud docs to local
      • not just removed - open/save docs to local

If someone removes iCloud - wouldn't the calls to ubiquitous URL return nil? If that's the case how do I migrate the docs back to local storage? I'll create a user pref for now but seems a bit of a workaround.

I feel like I'm missing something obvious here so if anyone can see it, please chime in.

How can two developers work together on an iOS app without incorporating a company?

10 votes

Let's say two friends want to get together and build an iOS app. They want to offer the app for free and aren't looking to derive income from the project. (Alternatively a variation on this is that they will charge 99 cents for the app but don't expect it to drive a lot of sales - they are just doing it as a learning project / for the experience).

In terms of how they would setup their development program membership with Apple, it seems that Apple would advise that they incorporate a company for this and register for iOS Company account since there's multiple developers. But there are legal and accounting fees associated with incorporating a company that a free app (or low yielding app) might not offset.

So they come up with an idea of just having one of the two developers sign up for an individual account, release it under his name, and have the other be the "silent partner". If they yield a small amount of revenue they sign an agreement between them to agree to share the revenue.

Will this scenario actually work? Or will the "silent partner" not be able to build the application because he won't have the certificate? Or could he share the certificate?

It there a simpler way of doing this? It seems like a lot of hoops to jump through to work on a simple project with somebody.

You may want to look into setting it up as a 'joint venture.' A joint venture is usually set up for the purposes of some temporary project and the parties involved are to split the revenues/expenses according to some agreement. This agreement is called the 'joint venture agreement.' You can find examples online with a quick Google search.

You can apply for an EIN (Employer Identification Number) as a joint venture (using "Other" on the type of entity line), which can be used for Apple's financial information purposes for tax reporting. You can also register a "Doing Business As" (DBA) name, which depends on your local state/city rules. This would allow you to do business as something other than one of your individual names.

For tax purposes, joint venture participants just report their share of the income/expenses on their own return on Schedule C. You'd be doing this anyway, under the current scenario.

There isn't any fee to apply for an EIN, and it can be done online at the IRS website. Any fees relating to registering a DBA name depend on your local state/city.

Hope this helps! (I was an accountant for 13 years before I decided to follow my tech passion)

Is ARC really supported in iOS 4? The iOS 4.2 SDK is missing ARC-related symbols at linking time

8 votes

I've read and heard since ARC was first announced that it was a compile-time thing and would be backwards-compatible with iOS 4. I have successfully refactored my project to ARC using Xcode 4.2's automatic refactoring, and when compiled against the iOS 5.0 SDK, it works fine. However, if I try to compile against my iOS 4.2 SDK, it fails at link time, missing the following symbols:

  • _objc_retainAutoreleaseReturnValue
  • _objc_autoreleaseReturnValue
  • _objc_storeStrong
  • _objc_retain
  • _objc_release
  • _objc_retainAutoreleasedReturnValue

I checked, and these symbols are present in 5.0 but not 4.2:

iPhoneOS5.0.sdk/usr/lib $ find . -type f|xargs nm|grep -i _objc_retain$
00005ed0 T _objc_retain
000061d0 T _objc_retain

iPhoneOS4.2.sdk/usr/lib $ find . -type f|xargs nm|grep -i _objc_retain$
[... *crickets* ...]

Does this mean that Apple lied? I assume instead that I'm confused and doing something wrong, but I can't figure out what.

This is with the GM release of Xcode 4.2 (Build 4C199)

ARCis supported on iOS 4.3 and above. You need to use the iOS 5.x SDK but can select iOS 4.3 for the Deployment Target. The one thing that is not supported in 4.3 is automatic weak reference zeroing.

Oh, Xcode 4.2 is now out of NDA and can be used to submit apps.

App Store code runs different from Xcode/Device code on iPhone 3G

7 votes

I'm not sure how to explain this one. I submitted an update of one of my apps to the store yesterday. The first screenshot is how one particular screen appears running on my iPhone 3G, running iOS 4.2.1, downloaded from the App Store:

And the second image below is the exact same code, no changes have been made since the original submission, running on the exact same device tethered through Xcode.

The application runs fine on my iPhone 4, running iOS 5, downloaded from the App Store. So to recap:

  1. App obtained from App Store is flawed on iPhone 3G
  2. App obtained from App Store is good on iPhone 4
  3. App tethered through Xcode is good on iPhone 3G

These are not the only graphical inconsistencies, but they're all related to custom UITableViewCell code, which doesn't do anything more than push labels a few pixels in each directory, and has been working fine since day one. I have filed a report with idp-dts, and am waiting to hear back from them, but as the waiting list is usually a week or more, I'd rather figure this out on my own.

Any help/tips/guesses would be very appreciated!

iPhone 3G running App Store version of my application:

enter image description here

iPhone 3G running Xcode tethered version of my application:

enter image description here

Edit: This sounds quite similar to this problem: Building with LLVM and any optimization causes app to crash on startup The customer who contacted me was using a 2nd generation iPod Touch, the only other hardware other than the iPhone 3G that uses armv6.

Edit 2: Here's the snippet of code that sets the bounds of the color bar on the left side. There doesn't seem to be anything fishy going on in the code:

- (void)layoutSubviews {
    CGRect colorViewFrame = self.bounds;
    colorViewFrame.size.width = 6;
    colorViewFrame.origin.y += 3;
    colorViewFrame.origin.x -= 1;
    colorViewFrame.size.height -= 8;

    colorView.frame = colorViewFrame;
    ...
}

So here's what I found under Build Settings:

enter image description here

Which, having taken a class or two in compiler optimization, makes sense. Code debugging on a device should be left in its original state for debugging purposes, and code released should be optimized for speed and efficiency.

So here's the fun part. Changing my Debug setting to Fastest, Smallest:

enter image description here

Causes the issue to occur on my device while running tethered in Xcode.

Before filing a Radar or making any rash decisions and submitting non-optimized code to the App Store, is there anything else I should consider investigating? Was the compiler optimization really the underlying cause of the layout issues?

Edit: And if the optimization level is the issue, why does the optimized code run properly on my iPhone 4, but not properly on my iPhone 3G?

Edit 2: This problem sounds extremely similar to this answer: Building with LLVM and any optimization causes app to crash on startup

Edit 3: Heard back from Apple Radar, this is a known issue. Will be fixed in a future version of Xcode. Thanks for all the help guys!

Sharekit: url is modified while posting to facebook?

7 votes

I am posting image with image title and in the image title I have added url, I am able to share image and url also but,there is slight modification in the url.The = symbol is converted to %3D ,as shown below the both URLs(dummy url).

posted URL: http://....=418ioekVlhTIu2sr9qpdAQ==  

URL on Facebook http://...=418ioekVlhTIu2sr9qpdAQ%3D%3D

So is there any better way to post url and image in one post only or help me so that by doing some change in the code I should be able to share correct url in the Image title itself.

This happens because the URL format converts it's reserved special characters to HTML entity codes (percent escaping) as shown in here: http://www.w3schools.com/tags/ref_ascii.asp

you have 2 options to pass the URL string correctly:

  1. On the receiver side (after the URL Request is sent by the client), decode the URL string that you received, this will normalize the string back to normal.

  2. Use the POST method of html instead of the GET method to store your parameters. although I'm not sure you have an option for that.

On the iOS obj-c, Conversion between URL Percent escapes is done like so-

[normalText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[encodedText stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

iOS 5 Core Data freeze

7 votes

I try to do the following simple thing:

NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];

Nothing fancy. But this freezes in iOS 5, it works fine in iOS 4. I don't get exceptions, warnings or errors; my app just simply freezes.

Please help me out! I'm dying here! ;)

Thanks for the hints given in this page on how to solve this freezing issue which appeared on upgrading from iOS4. It has the most annoying problem I have found since I started programming on iOS.

I have found a quick solution for cases where there are just a few calls to the context from other threads.

I just use performSelectorOnMainThread:

    [self performSelectorOnMainThread:@selector(stateChangeOnMainThread:) withObject: [NSDictionary dictionaryWithObjectsAndKeys:state, @"state", nil] waitUntilDone:YES];

To detect the places where the context is called from another thread you can put a breakpoint on the NSLog on the functions where you call the context as in the following piece of code and just use performSelectorOnMainThread on them.

if(![NSThread isMainThread]){
     NSLog("Not the main thread...");
}

I hope that this may be helpful...

Back button does not appear in navigation bar until you rotate

7 votes

I have three view controllers: A -> B -> C managed by a navigation controller. A is a transient view controller. It asks the server for something. If the server says everyhing is OK, then A pushes B onto the stack. B must hide the back button because I don't want users to manually go back to A.

// B view controller
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = YES;
    self.title = @"B";
}

B then pushes C onto the stack when the user taps a table cell.

// B view controller
- (void)tableView:(UITableView *)tableView 
didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
    C *c = [[C alloc] 
        initWithStyle:UITableViewStyleGrouped
    ];
    [self.navigationController 
        pushViewController:c 
        animated:YES
    ];
    [c release];
}

.

// C view controller
- (void) viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = NO;
    self.title = @"C";
}

If all goes well, the flow should look like this:

-------------    -------------    -------------
|_____A_____|    |_____B ____|    | <B|__ C___|
|           | => |           | => |           |
| loading...|    |   cells   |    |   detail  |
|           |    |           |    |           |
-------------    -------------     -----------

For some reason, C does not show a back button to go back to B until I rotate the device. Once rotated, the back button appears in all orientations. The problem seems to stem from B hiding the back button and C trying to reveal it again, because If I don't let B hide it, I don't have this problem. So how do I get C to show the back button without forcing the user to rotate the device like a monkey?

Update

  • Broken on two different Verizon iPhone 4 both on iOS 4.2.10
  • Fine on AT&T iPhone 3GS on iOS 5.0
  • Fine on AT&T iPhone 4 on iOS 4.3

After some searching I found this solution for iPhone 4.2 (since you posted that it works on later versions) on some old forum post.

-(void)viewDidLoad { 
                     [super viewDidLoad];
                     self.navigationItem.hidesBackButton = YES; 
                   }

-(void)viewDidAppear:(BOOL)animated { 
                                       [super viewDidAppear:animated];
                                       self.navigationItem.hidesBackButton = NO; 
                                    }

Mayhaps this will help you out. (Check this out: Back button don't appear in navigationController)