Best ios questions in April 2012

How to write iOS app purely in C

46 votes

I read here Learn C Before Objective-C?

Usually I then replace some Obj-C code with pure C code (after all you can mix them as much as you like, the content of an Obj-C method can be entirely, pure C code)

Is this true?

Could I build an iPhone app purely in the C programming language?

Damn, it took me a while but I got it:

main.m:

int main(int argc, char *argv[])
{
    @autoreleasepool {
        // Nothing here has changed, just simply launching the app
        return UIApplicationMain(argc, argv, nil, @"AppDelegate");
    }
}

AppDelegate.m:

#import <objc/runtime.h>
#import <objc/message.h>

// This is equivalent to creating a @class with one public variable named 'window'.
struct AppDel
{
    Class isa;

    id window;
};

// This is a strong reference to the class of the AppDelegate 
// (same as [AppDelegate class])
Class AppDelClass;

// this is the entry point of the application, same as -application:didFinishLaunchingWithOptions:
// note the fact that we use `void *` for the 'application' and 'options' fields, as we need no reference to them for this to work. A generic id would suffice here as well.
BOOL AppDel_didFinishLaunching(struct AppDel *self, SEL _cmd, void *application, void *options)
{
    // we +alloc and -initWithFrame: our window here, so that we can have it show on screen (eventually).
    // this entire method is the objc-runtime based version of the standard View-Based application's launch code, so nothing here really should surprise you.
    // one thing important to note, though is that we use `sel_getUid()` instead of @selector().
    // this is because @selector is an objc language construct, and the application would not have been created in C if I used @selector.
    self->window = objc_msgSend(objc_getClass("UIWindow"), sel_getUid("alloc"));
    self->window = objc_msgSend(self->window, sel_getUid("initWithFrame:"), (struct CGRect) { 0, 0, 320, 480 });

    // here, we are creating our view controller, and our view. note the use of objc_getClass, because we cannot reference UIViewController directly in C.
    id viewController = objc_msgSend(objc_msgSend(objc_getClass("UIViewController"), sel_getUid("alloc")), sel_getUid("init"));

    // creating our custom view class, there really isn't too much 
    // to say here other than we are hard-coding the screen's bounds, 
    // because returning a struct from a `objc_msgSend()` (via 
    // [[UIScreen mainScreen] bounds]) requires a different function call
    // and is finicky at best.
    id view = objc_msgSend(objc_msgSend(objc_getClass("View"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), (struct CGRect) { 0, 0, 320, 480 });

    // here we simply add the view to the view controller, and add the viewController to the window.
    objc_msgSend(objc_msgSend(viewController, sel_getUid("view")), sel_getUid("addSubview:"), view);
    objc_msgSend(self->window, sel_getUid("setRootViewController:"), viewController);

    // finally, we display the window on-screen.
    objc_msgSend(self->window, sel_getUid("makeKeyAndVisible"));

    return YES;
}

// note the use of the gcc attribute extension (constructor). 
// Basically, this lets us run arbitrary code before program startup,
// for more information read here: http://stackoverflow.com/questions/2053029
__attribute__((constructor))
static void initAppDel()
{
    // This is objc-runtime gibberish at best. We are creating a class with the 
    // name "AppDelegate" that is a subclass of "UIResponder". Note we do not need
    // to register for the UIApplicationDelegate protocol, that really is simply for 
    // Xcode's autocomplete, we just need to implement the method and we are golden.
    AppDelClass = objc_allocateClassPair(objc_getClass("UIResponder"), "AppDelegate", 0);

    // Here, we tell the objc runtime that we have a variable named "window" of type 'id'
    class_addIvar(AppDelClass, "window", sizeof(id), 0, "@");

    // We tell the objc-runtime that we have an implementation for the method
    // -application:didFinishLaunchingWithOptions:, and link that to our custom 
    // function defined above. Notice the final parameter. This tells the runtime
    // the types of arguments received by the function.
    class_addMethod(AppDelClass, sel_getUid("application:didFinishLaunchingWithOptions:"), (IMP) AppDel_didFinishLaunching, "i@:@@");

    // Finally we tell the runtime that we have finished describing the class and 
    // we can let the rest of the application use it.
    objc_registerClassPair(AppDelClass);
}

View.m

#include <objc/runtime.h>

// This is a strong reference to the class of our custom view,
// In case we need it in the future.
Class ViewClass;

// This is a simple -drawRect implementation for our class. We could have 
// used a UILabel  or something of that sort instead, but I felt that this 
// stuck with the C-based mentality of the application.
void View_drawRect(id self, SEL _cmd, struct CGRect rect)
{
    // We are simply getting the graphics context of the current view, 
    // so we can draw to it
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Then we set it's fill color to white so that we clear the background.
    // Note the cast to (CGFloat []). Otherwise, this would give a warning
    //  saying "invalid cast from type 'int' to 'CGFloat *', or 
    // 'extra elements in initializer'. Also note the assumption of RGBA.
    // If this wasn't a demo application, I would strongly recommend against this,
    // but for the most part you can be pretty sure that this is a safe move 
    // in an iOS application.
    CGContextSetFillColor(context, (CGFloat []){ 1, 1, 1, 1 });

    // here, we simply add and draw the rect to the screen
    CGContextAddRect(context, (struct CGRect) { 0, 0, 320, 480 });
    CGContextFillPath(context);

    // and we now set the drawing color to red, then add another rectangle
    // and draw to the screen
    CGContextSetFillColor(context, (CGFloat []) { 1, 0, 0, 1 });
    CGContextAddRect(context, (struct CGRect) { 10, 10, 20, 20 });
    CGContextFillPath(context);
}

// Once again we use the (constructor) attribute. generally speaking, 
// having many of these is a very bad idea, but in a small application 
// like this, it really shouldn't be that big of an issue.
__attribute__((constructor))
static void initView()
{
    // Once again, just like the app delegate, we tell the runtime to 
    // create a new class, this time a subclass of 'UIView' and named 'View'.
    ViewClass = objc_allocateClassPair(objc_getClass("UIView"), "View", 0);

    // and again, we tell the runtime to add a function called -drawRect: 
    // to our custom view. Note that there is an error in the type-specification
    // of this method, as I do not know the @encode sequence of 'CGRect' off 
    // of the top of my head. As a result, there is a chance that the rect 
    // parameter of the method may not get passed properly.
    class_addMethod(ViewClass, sel_getUid("drawRect:"), (IMP) View_drawRect, "v@:");

    // And again, we tell the runtime that this class is now valid to be used. 
    // At this point, the application should run and display the screenshot shown below.
    objc_registerClassPair(ViewClass);    
}

It's ugly, but it works.

If you would like to download this, you can get it from my dropbox here

ScreenShot

How dangerous is it to compare floating point values?

30 votes

I know UIKit uses CGFloat because of the resolution independent coordinate system.

But every time I want to check if for example frame.origin.x is 0 it makes me feel sick:

if (theView.frame.origin.x == 0) {
    // do important operation
}

Isn't CGFloat vulnerable to false positives when comparing with ==, <=, >=, <, >? It is a floating point and they have unprecision problems: 0.0000000000041 for example.

Is Objective-C handling this internally when comparing or can it happen that a origin.x which reads as zero does not compare to 0 as true?

First of all, floating point values are not "random" in their behavior. Exact comparison can and does make sense in plenty of real-world usages. But if you're going to use floating point you need to be aware of how it works. Erring on the side of assuming floating point works like real numbers will get you code that quickly breaks. Erring on the side of assuming floating point results have large random fuzz associated with them (like most of the answers here suggest) will get you code that appears to work at first but ends up having large-magnitude errors and broken corner cases.

First of all, if you want to program with floating point, you should read this:

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Yes, read all of it. If that's too much of a burden, you should use integers/fixed point for your calculations until you have time to read it. :-)

Now, with that said, the biggest issues with exact floating point comparisons come down to:

  1. The fact that lots of values you may write in the source, or read in with scanf or strtod, do not exist as floating point values and get silently converted to the nearest approximation. This is what demon9733's answer was talking about.

  2. The fact that many results get rounded due to not having enough precision to represent the actual result. An easy example where you can see this is adding x = 0x1fffffe and y = 1 as floats. Here, x has 24 bits of precision in the mantissa (ok) and y has just 1 bit, but when you add them, their bits are not in overlapping places, and the result would need 25 bits of precision. Instead, it gets rounded (to 0x2000000` in the default rounding mode).

  3. The fact that many results get rounded due to needing infinitely many places for the correct value. This includes both rational results like 1/3 (which you're familiar with from decimal where it takes infinitely many places) but also 1/10 (which also takes infinitely many places in binary, since 5 is not a power of 2), as well as irrational results like the square root of anything that's not a perfect square.

  4. Double rounding. On some systems (particularly x86), floating point expressions are evaluated in higher precision than their nominal types. This means that when one of the above types of rounding happens, you'll get two rounding steps, first a rounding of the result to the higher-precision type, then a rounding to the final type. As an example, consider what happens in decimal if you round 1.49 to an integer (1), versus what happens if you first round it to one decimal place (1.5) then round that result to an integer (2). This is actually one of the nastiest areas to deal with in floating point, since the behavior of the compiler (especially for buggy, non-conformant compilers like GCC) is unpredictable.

  5. Transcendental functions (trig, exp, log, etc.) are not specified to have correctly rounded results; the result is just specified to be correct within one unit in the last place of precision (usually referred to as 1ulp).

When you're writing floating point code, you need to keep in mind what you're doing with the numbers that could cause the results to be inexact, and make comparisons accordingly. Often times it will make sense to compare with an "epsilon", but that epsilon should be based on the magnitude of the numbers you are comparing, not an absolute constant. (In cases where an absolute constant epsilon would work, that's strongly indicative that fixed point, not floating point, is the right tool for the job!)

Edit: In particular, a magnitude-relative epsilon check should look something like:

if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y))

Where FLT_EPSILON is the constant from float.h (replace it with DBL_EPSILON for doubles or LDBL_EPSILON for long doubles) and K is a constant you choose such that the accumulated error of your computations is definitely bounded by K units in the last place (and if you're not sure you got the error bound calculation right, make K a few times bigger than what your calculations say it should be).

Finally, note that if you use this, some special care may be needed near zero, since FLT_EPSILON does not make sense for denormals. A quick fix would be to make it:

if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y) || fabs(x-y) < FLT_MIN)

and likewise substitute DBL_MIN if using doubles.

App is behaving different on iPhone 5.1 simulator and real iPhone 4 with iOS 5.1

24 votes

In a nutshell:

When the title of a back-button of a navigation controller gets changed, in some cases the old title is stuck and the new title will not be displayed. This happens only in some reproduceable situations, while it works as designed in other situations.

It depends on the hardware

  • The error happens on iPhone 3G (iOS 4.2.1) and in the simulator (iOS 5.1)
  • With identical sourcecode there is no error on iPhone 4 (iOS 5.1)

It depends on the word that is written to the title

  • When the button is created, and it gets from my selfwritten creating-method the same word as title that it would have got automatically (i.e. the title of the previous page on the navigation controller's stack), and when the other circumstances match, then, when trying to change the button's title at a later moment, the old text is stuck and the new title will not show up.
  • When at creation time the button gets a word as title that is different from its default-title, then every later changes of its title works fine, as long as you don't assign the default-title to it.
  • If, after a lot of successfull changes with many different titles, you put the word on the buttons title, that was its default title, then this word is stuck. Later changes will not be accepted (without any message, and only if the other circumstances match)

It depends on whether the button was invisible in the meantime or not.

  • If another view was pushed on the navigation controllers stack, so that the old page with the flawed button became hidden by the new page, and when the new page was popped from the stack later again which makes the button visible again, (and when the other circumstances match) then the old text was stuck and the trial to change it is ignored (without any message).
  • If the button was newer hidden, changing its title never is no problem. I works always.

The correct title is visible during animation

  • When the attempt to change the back-button's title was ignored due to the combination of the circumstances described above, the proper title anyhow becomes visible for about 0.3 seconds when this back-button is hit and the page's slide-to-right-animation is processed. At the beginning of the animation the old stuck title is replaced by the proper title, and the correct title is visible during the animation.

Detailed description

It's about the text on a UINavigationController's back button. I change this buttons title in dependence of new language-settings. At the moment my app has a maximum of 3 view controllers in the navigation controllers stack. Each of them is a different subclass of `UITableViewController.

Table 1, named GeneralTableVC is the root view on the stack. It has no back button. It gives the user a summary of what he has stored inside the app and it displayes a toolbar with a settings-button.

It is the navitation controller who provides this Toolbar that is visible in Table 1. It is set to invisible in Table 2 and 3. At the moment there is only one button in that toolbar named "Settings". Touching this Settings-Button will push Table 2 onto the stack.

Table 2, named SettingsTabVC has a back button, and this is the one that makes problems in the simulator but works fine on my real iPhone 4 running iOS 5.1.

By touching the first row of Table 2 a new Table (Table 3) will be created and pushed onto the stack.

Table 3, named LangSelectTableVC also has a back button, but this one works pretty fine in both devices, iPhone simulator and real iPhone 4.

Table 3 is a language selection table that displayes a list of all available languages (at the moment just english and german). Touching a row changes settings immediately. The active view (Table 3) will be redrawn, and within a few milliseconds all texts on screen appear in the new language.

Redrawing the table itself is no problem, as well as the title in the navigation bar. But the text on the back button must be translated too, and this is a little bit tricky. I have done the very same trick on both back-buttons, and it works fine for the button visible on Table 3 who is directing to Table 2. But with the very same code there is a problem in the simulator (but not on a real iPhone) with the button on Table 2 who is directing to Table 1.

I give you some code-snippets and some screenshots to show you what I've done and what is happening:


Sourcecode

ARC (automatic reference counting) is in use.

I did define a redraw-Protocol:

Protocols.h

#ifndef ToDo_Project_Protocols_h
#define ToDo_Project_Protocols_h

@protocol redrawProt
- (void) mustRedraw;
@end

#endif

This is the header of Table 1:

GeneralTableVC.h

#import <UIKit/UIKit.h>
#import "Protocols.h"
// some other imports

@interface GeneralTabVC : UITableViewController <redrawProt>

@property id<redrawProt>   parent;
@property Boolean          mustRedrawMyself;
@property NSString*        backTitle;
@property UIBarButtonItem* myBackButton;
@property UIBarButtonItem* parBackButton;

- (id) initWithParent:(id<redrawProt>)par andBackTitle:(NSString*)bT andBackButton:(UIBarButtonItem*)bB;

@end

The header files of the other Tables, SettingsTabVC.h and LangSelectTabVC.h define the same properties and an identical init-function

The program starts here:

part of AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // some code
    GeneralTabVC* genTabCon = [[GeneralTabVC alloc] initWithParent:nil andBackTitle:nil andBackButton:nil];
    UINavigationController* navCon = [[UINavigationController alloc] initWithRootViewController:genTabCon];
    // some other code
}

Next comes the implementation of Table 1 (GeneralTableVC.m). The Code in Table 2 (SettingsTabVC.m) and Table 3 (LangSelectTabVC.m) is analogously the same. I don't show those parts of code that implements the protocol UITableViewDataSource. I think those parts are not really important for explaining the problem.

In this code you will find the macro LocalizedString(keyword) which does exactly the same as NSLocalizedString(keyword,comment), which is translating the keyword into the desired language. My version of this macro uses a different bundel for translation (not the main bundle)

GeneralTableVC.m

#import "GeneralTabVC.h"
#import "SettingsTabVC.h"

#define MYTITLE @"summary"

id<redrawProt>   parent;
Boolean          mustRedrawMyself;
NSString*        backTitle;
UIBarButtonItem* myBackButton;
UIBarButtonItem* parBackButton;

@interface GeneralTabVC ()

@end

@implementation GeneralTabVC

@synthesize parent, mustRedrawMyself, backTitle, myBackButton, parBackButton;

- (void) mustRedraw {
    self.mustRedrawMyself = YES;
}

- (void) redraw {
    if ((self.parBackButton) && (self.backTitle)) {
        // Important!
        // here I change the back buttons title!
        self.parBackButton.title = LocalizedString(self.backTitle);
    }
    if (self.parent) {
        [self.parent mustRedraw];
    }
    self.title = LocalizedString(MYTITLE);
    [self.tableView reloadData];
    self.mustRedrawMyself = NO;
}

- (id) initWithParent:(id<redrawProt>)par andBackTitle:(NSString*)bT andBackButton:(UIBarButtonItem *)bB {
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {
        self.parent = par;
        self.mustRedrawMyself = NO;
        self.backTitle = bT;
        self.parBackButton = bB;
    }
    return self;
}

- (void) toolbarInit {
    // this method exists only in Table 1, not in other tables
    // it creates a UIBarButtonItem, adds it to self.toolbarItems
    // and makes it visible
}

- (void)SettingsAction:(id)sender {
    // this method exists only in Table 1, not in other tables
    // it will be executed after the user tabs on the settings-
    // button in the toolbar
    SettingsTabVC* setTabCon = [[SettingsTabVC alloc] initWithParent:self andBackTitle:MYTITLE andBackButton:self.myBackButton];
    [self.navigationController pushViewController:setTabCon animated:YES];
}

- (void) viewDidLoad {
    [super viewDidLoad];
    self.title = LocalizedString(MYTITLE);    
    // I want an Edit-Button. Localization of this button is
    // not yet done. At the moment is uses the systems language,
    // not the apps language.
    self.navigationItem.rightBarButtonItem = self.editButtonItem;
    [self toolbarInit];    
}

- (void) viewWillAppear:(BOOL)animated {    
    // this is an important method! Maybe here is the reason for 
    // my problem! 
    [super viewWillAppear:animated];
    // When ever this controllers view is going to appear, and  
    // when ever it is necessary to redraw it in a new language,
    // it will redraw itself:
    if (self.mustRedrawMyself) {
        [self redraw];
    }

    // And here comes the buggy back button:
    // When ever this controllers view is going to appear,
    // a new back button will be created with a title in the
    // new language:
    UIBarButtonItem* BB = [[UIBarButtonItem alloc] init];
    BB.title = LocalizedString(MYTITLE);
    self.myBackButton = BB;
    self.navigationItem.backBarButtonItem = self.myBackButton;
}

- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // show toolbar:
    [self.navigationController setToolbarHidden:NO animated:YES];
}

// next methods are about InterfaceOrientation and the
// UITableViewDataSource protocoll. They are not important
// for the problem.

// but maybe the very last method is important. It comes in
// different versions in the three implementation files:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // This is the version of GeneralTableVC.m (Table 1)
    // It does nothing (at the actual stage of expansion, in later
    // versions it will start the main business logic of this app)
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // This is the version of SettingsTableVC.m (Table 2)
    // Tabbing onto row 0 of section 0 will push the
    // language-selection-table (Table 3) on screen:
    if (indexPath.section == 0) {
        if (indexPath.row == 0) {
            // create Table 3:
            LangSelectTabVC* langTabCon = [[LangSelectTabVC alloc] initWithParent:self andBackTitle:MYTITLE andBackButton:self.myBackButton];
            [self.navigationController pushViewController:langTabCon animated:YES];
        } else {
            // do something else (nothing at this stage of expansion)
        }
    } else {
        // do something else (nothing at this stage of expansion)
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // This is the version of LangSelectTableVC.m (Table 3)

    // here I do some magic to select and store the new language.
    // Part of this magic is transforming indexPath.row
    // into a valid language-code, putting it into the 
    // settings-object, and registering this object to 
    // NSUserDefaults
}

@end

Screenshots

Starting the app on iPhone 5.1 simulator will put Table 1 (GeneralTableVC) on screen:

after starting the app

In the toolbar on the screens button, on its right side, you find a settings-button. Pressing this button brings the next table on screen:

settings-screen

Watch the back button in the title bar. It displays the text "Summary", which is correct, since the previous table title was "Summary".

Now we tab onto the first row ("Language English >"):

before changing language

Everything is fine. Now let's change the language. Tab on "German":

after changing language

Wow! Everything is in German now. Even the back button has changed from "Settings" to "Einstellungen".

Lets tab on that "Einstellungen" back button:

back button has wrong language in simulator

Almost everthing is fine now; everything has changed into german. Everything but the back button, which still says "Summary" instead of "Überblick". And I do not understand why, because when I do exactly the same steps with exactly the same sourcecode on my real iPhone 4, the last screen looks like this:

correct langugage on real iPhone

Mind the text on the back-button. On a real iPhone 4 it is the german word "Überblick" (which is what I want), but in the simulator it is the english word "Summary". And this means to me, on some phones (like my iPhone 4) the user gets what is expected, but maybe on some other phones (maybe iPhone 4S) the user gets a buggy display.

Has anybody an idea what is wrong with my code?


EDITs

Edit: 2012-04-06 09:04 +02:00 (central european summer time)

I did manage to test my app on an other piece of hardware, an old iPhone 3G (iOS 4.2.1) On the old iPhone my app is behaving exactly the same way like in the simulator. Running the same app on iPhone 4 produces a different behaviour.

To be more precise:

  • On iPhone 4 (iOS 5.1): App is doing what I want, no faulty behavior.
  • On Simulator (iOS 5.1): App displays wrong title on a navigation controllers back button.
  • On iPhone 3G (iOS 4.2.1): App shows the same faulty behaviour as in the simulator.

Edit: 2012-04-07 10:14 +02:00 (central european summer time)

By watching the transition on the iPhone 3G, I fond out something interesting and maybe helpfull: When I tab on the button with the wrong text, the following happens:

  1. The wrong text is replaced by the correct text
  2. After this replacement the view dissapears animated (sliding to the right) and the underlaying view becomes visible. This transition has a duration of aproximately 0.3 seconds, and in this short interval the correct text is visible in all hardware-iPhones and in the simulator too.

But the question still is: Why is the wront text displayed in iPhone 3G and Simulator? Why is the correct text always visible in iPhone 4?

To me it looks, as if there was two buttons at the same place, one over the other. In iPhone 4 "my" custom button is in front, hiding the old systemgenerated button, but in the simulator and in iPhone 3G the old systemgenerated button is in front, hiding my custom button. But: Even if my hidden custom button is bigger (wider) than the systemgenerated, nothing of it is visible. Only when the slide-out animation starts, my button becomes visible.


Edit: 2012-04-07 16:38 +02:00 (central european summer time)

Next interesting fact:

This is what happened until now:

When the button appers for the first time (2nd screenshot, see below), I put a word as title on it, that is identic to the word it would have become before from the system. Then the user selects some action, and this button is hidden by another view. After another user-action the button is revealed again, it now it shall get a new word as title (same meaning, but new language), but on iPhone 3G and on the simulator the old title is "stronger". The new title will not be displayed. The old title sticks there.

This does not happen if at first appearence I write a word as title onto the button, that is different from the systemgenerated title. If the first title is different from the default-title, a later change will be executed on all iPhones and on the simulator.

That makes me believe, that iOS does some kind of "optimization": If, at first appearance of the button, the custom title is identic to the systemgenerated title, than a later change of the buttons title will be ignored, but only on iPhone 3G and simulator. On iPhone 4 a later change will be allowed in any case.

But setting a different title at the beginning to prevent the app from its faulty behaviour is not an option.

Apple support did answer

I contacted Apple support for that issue and they did answer.

The problem was, that the navigation bar holds all back-buttons for views on the navigation controllers stack and all this buttons needs to be updated at the same time. Updating the views that lay on the stack within their viewWillAppear-Methods is good, but trying to update the back-button at this place is no good idea.


The solution:

Extend the interface of UIViewController:

@interface UIViewController (extended)
    - (NSString *)localizedKey;
@end

For each UIViewController that puts a view on the UINavigationController's stack implement this method:

- (NSString*) localizedKey {
    return @"a title-keyword";
}

Do not mess around with UIBarButtonItem or self.navigationItem.backBarButtonItem in any of the UIViewControllers.

When if comes that titles need to be changed, do it for ALL back buttons with this snippet of code (remember: LocalizedString(key) is a self-written macro similar to NSLocalizedString(key,comment)):

NSArray* vcs = [self.navigationController viewControllers];
for (UIViewController* vc in vcs) {
    vc.navigationItem.backBarButtonItem.title = LocalizedString([vc localizedKey]);
    vc.title = LocalizedString([vc localizedKey]);
}

Verbatim answer of Apple Support:

We're fighting the navigation bar in forcing an update at the wrong time. Notice all the views in each view controller get updated properly. So the navigation bar needs special attention to get what we want.

To get this to work, you need to change the back buttons to all the view controllers on the stack at once, (at the time the user selects a language) instead of when they each appear via "viewWillAppear".

This requires the ability to obtain the localized key for that button in a public way. I introduced a category to UIViewController to easily adopt this:

@interface UIViewController (extended)
- (NSString *)localizedKey;
@end

Then your LangSelectTabVC class can change all the back buttons at once. This approach will make the button titles redraw correctly.

So in viewWillAppear, you won't have to update each back button. Doing it there appears to be too late for UIKit to catch that update. You also re-create a new back button when an update occurs. This is not necessary, just take the current one and change it's title:

NSArray *vcs = [self.navigationController viewControllers];
for (UIViewController *vc in vcs)
{
vc.navigationItem.backBarButtonItem.title = LocalizedString([vc localizedKey]);
vc.title = LocalizedString([vc localizedKey]);
}

I've attached a modified project showing this workaround.

Objective-C - Disadvantages to Bridging With C++?

14 votes

So, I was bored today, and decide to mess with C++/Obj-C interpolation, and I found a way to create a very interesting setup.

@protocol NSCPPObj <NSObject>

-(id) init;
-(id) initWithInt:(int) value;
-(int) somethingThatReturnsAValue;
-(void) doSomething;

@end

class NSCPPObj : objc_object {
public:    
    static Class cls();

    int iVar;

    NSCPPObj();
    NSCPPObj(int);

    int somethingThatReturnsAValue();
    void doSomething();
};

As you can see, the interface is quite straightforward, and easy to understand. We create two (almost) identical interfaces, one for a C++ object, and another for a Obj-C protocol.

Now, I found a way to implement this, but brace yourself, this gets ugly:

// NSCPPObj.mm
#import <objc/runtime.h>
#import <iostream>

#import "NSCPPObject.h"

Class NSCPPObj_class = nil;

__attribute__((constructor))
static void initialize()
{
    NSCPPObj_class = objc_allocateClassPair([NSObject class], "NSCPPObj", 0);

    class_addMethod(NSCPPObj_class->isa, @selector(alloc), imp_implementationWithBlock(^(id self) {
        return class_createInstance(NSCPPObj_class, sizeof(struct NSCPPObj));
    }), "@@:");

    class_addMethod(NSCPPObj_class, @selector(init), imp_implementationWithBlock(^(id self) {
        return self;        
    }), "@@:");

    class_addMethod(NSCPPObj_class, @selector(initWithInt:), imp_implementationWithBlock(^(id self, int value) {
        ((struct NSCPPObj *) self)->iVar = value;

        return self;
    }), "@@:i");

    class_addMethod(NSCPPObj_class, @selector(doSomething), imp_implementationWithBlock(^(id self) {
        ((struct NSCPPObj *) self)->doSomething();
    }), "v@:");
    class_addMethod(NSCPPObj_class, @selector(somethingThatReturnsAValue), imp_implementationWithBlock(^(id self) {
        return ((struct NSCPPObj *) self)->somethingThatReturnsAValue();
    }), "i@:");

    objc_registerClassPair(NSCPPObj_class);
}

Class NSCPPObj::cls()
{
    return NSCPPObj_class;
}

NSCPPObj::NSCPPObj()
{
    this->isa = NSCPPObj_class;
    [((id<NSCPPObj>) this) init];
}

NSCPPObj::NSCPPObj(int value)
{
    this->isa = NSCPPObj_class;
    [((id<NSCPPObj>) this) initWithInt:value];
}

void NSCPPObj::doSomething()
{
    std::cout << "Value Is: " << [((id<NSCPPObj>) this) somethingThatReturnsAValue] << std::endl;
}

int NSCPPObj::somethingThatReturnsAValue()
{
    return iVar;
}

I'll summarize what this does:

  1. Allocates a Class Pair
  2. Adds all class and instance methods to the object
  3. Registers the class Pair

Now, as you can see, this isn't very flexible, but it does work, and it's a two-way street:

id<NSCPPObj> obj = [[NSCPPObj::cls() alloc] initWithInt:15];
[obj doSomething];

NSLog(@"%i", [obj somethingThatReturnsAValue]);
NSLog(@"%@", obj);

NSCPPObj *objAsCPP = (__bridge NSCPPObj *) obj;

objAsCPP->doSomething();
std::cout << objAsCPP->somethingThatReturnsAValue() << std::endl;

You can also create the object by using new NSCPPObj(15), but remember to delete it! Obviously, this can work in a ARC or non-ARC environment, but ARC requires a few extra bridged casts.

So, I come to the real question:
What are the pros/cons of this design structure? I can list a few off of the top of my head:

Pros:

  1. Operator Overloading with C++
  2. Dynamic method binding with ObjC
  3. Can be constructed in either a C++ or ObjC fashion

Cons:

  1. Hard-to-read implementation
  2. Selectors & bindings must be added for every C++ implementation added to the interface
  3. Class object cannot be referenced directly

So, after all that, would you recommend this design structure in an application? and why.

So, after all that, would you recommend this design structure in an application? and why.

No.

It is a really nice bit of code; I particularly like the use of imp_implementationWithBlock() (but I admit I might be partial to that particular feature of the runtime ;). And, of course, explorations like this are always an incredibly valuable learning tool.

The issue, in the context of "real world paying project" use, is that you are effectively creating a relatively generic bridge that will then have to have specific bridges at either end to interface with either typical C++ libraries or typical Objective-C APIs/libraries. To put it another way, you have effectively created a new runtime derived from an amalgamation of two existing runtimes.

And, as you point out in the Cons, you pretty much have to touch, wrap, modify and/or debug a shim on top of every C++ class you want to bring into this pattern.

In working with quite a bit of Objective-C++ code over the last 20+ years, a bridge like this is generally more trouble than it is worth. You would likely be better off -- spend less time writing and debugging code -- creating simple Objective-C wrappers around the C++ (or C, frankly) APIs that can then be integrated with and consumed by the targeted system's Objective-C frameworks.

How do I rewrite the UIDatePicker component?

11 votes

I've noticed that the UIDatePicker doesn't work with NSHebrewCalendar in iOS 5.0 or 5.1. I've decided to try and write my own. I'm confused as to how to populate the data and how to maintain the labels for the dates in a sane and memory efficient manner.

How many rows are there actually in each component? When do the rows get "reloaded" with new labels?

I'm going to give this a shot, and I'll post as I find out, but please post if you know anything.

First off, thanks for filing the bug about UIDatePicker and the Hebrew calendar. :)

As you've discovered, creating a functioning date picker is a difficult problem, because there are bajillions of weird edge cases to cover. The Hebrew calendar is particularly weird in this regard, having an intercalary month (Adar I), whereas most of western civilization is used to a calendar that only adds an extra day about once every 4 years.

That being said, creating a minimal Hebrew date picker isn't too complex, assuming you're willing to forego some of the niceties that UIDatePicker offers. So let's make it simple:

@interface HPDatePicker : UIPickerView

@property (nonatomic, retain) NSDate *date;

- (void)setDate:(NSDate *)date animated:(BOOL)animated;

@end

We're simply going to subclass UIPickerView and add support for a date property. We're going to ignore minimumDate, maximumDate, locale, calendar, timeZone, and all the other fun properties that UIDatePicker provides. This will make our job much simpler.

The implementation is going to start off with a class extension:

@interface HPDatePicker () <UIPickerViewDelegate, UIPickerViewDataSource>

@end

Simply to hide that the HPDatePicker is its own delegate and datasource.

Next we'll define a couple of handy constants:

#define LARGE_NUMBER_OF_ROWS 10000

#define MONTH_COMPONENT 0
#define DAY_COMPONENT 1
#define YEAR_COMPONENT 2

You can see here that we're going to hard-code the order of the calendar units. In other words, this date picker will always display things as Month-Day-Year, regardless of any customizations or locale settings that the user may have. So if you're using this in a locale where the default format would want "Day-Month-Year", then too bad. For this simple example, this will suffice.

Now we start the implementation:

@implementation HPDatePicker {
    NSCalendar *hebrewCalendar;
    NSDateFormatter *formatter;

    NSRange maxDayRange;
    NSRange maxMonthRange;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self setDelegate:self];
        [self setDataSource:self];

        [self setShowsSelectionIndicator:YES];

        hebrewCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSHebrewCalendar];
        formatter = [[NSDateFormatter alloc] init];
        [formatter setCalendar:hebrewCalendar];

        maxDayRange = [hebrewCalendar maximumRangeOfUnit:NSDayCalendarUnit];
        maxMonthRange = [hebrewCalendar maximumRangeOfUnit:NSMonthCalendarUnit];

        [self setDate:[NSDate date]];
    }
    return self;
}

- (void)dealloc {
    [hebrewCalendar release];
    [formatter release];
    [super dealloc];
}

We're overriding the designated initializer to do some setup for us. We set the delegate and datasource to be ourself, show the selection indicator, and create a hebrew calendar object. We also create an NSDateFormatter and tell it that it should format NSDates according to the hebrew calendar. We also pull out a couple of NSRange objects and cache them as ivars so we don't have to constantly be looking things up. Finally, we initialize it with the current date.

Here are the implementations of the exposed methods:

- (void)setDate:(NSDate *)date {
    [self setDate:date animated:NO];
}

-setDate: just forwards on to the other method

- (NSDate *)date {
    NSDateComponents *c = [self selectedDateComponents];
    return [hebrewCalendar dateFromComponents:c];
}

Retrieve an NSDateComponents representing whatever is selected at the moment, turn it into an NSDate, and return that.

- (void)setDate:(NSDate *)date animated:(BOOL)animated {
    NSInteger units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
    NSDateComponents *components = [hebrewCalendar components:units fromDate:date];

    {
        NSInteger yearRow = [components year] - 1;
        [self selectRow:yearRow inComponent:YEAR_COMPONENT animated:animated];
    }

    {
        NSInteger middle = floor([self pickerView:self numberOfRowsInComponent:MONTH_COMPONENT] / 2);
        NSInteger startOfPhase = middle - (middle % maxMonthRange.length) - maxMonthRange.location;
        NSInteger monthRow = startOfPhase + [components month];
        [self selectRow:monthRow inComponent:MONTH_COMPONENT animated:animated];
    }

    {
        NSInteger middle = floor([self pickerView:self numberOfRowsInComponent:DAY_COMPONENT] / 2);
        NSInteger startOfPhase = middle - (middle % maxDayRange.length) - maxDayRange.location;
        NSInteger dayRow = startOfPhase + [components day];
        [self selectRow:dayRow inComponent:DAY_COMPONENT animated:animated];
    }
}

And this is where fun stuff starts happening.

First, we'll take the date we were given and ask the hebrew calendar to break it up into a date components object. If I give it an NSDate that corresponds to the gregorian date of 4 Apr 2012, then the hebrew calendar is going to give me an NSDateComponents object that corresponds to 12 Nisan 5772, which is the same day as 4 Apr 2012.

Based on this information, I figure out which row to select in each unit, and select it. The year case is simple. I simply subtract one (rows are zero-based, but years are 1-based).

For months, I pick the middle of the rows column, figure out where that sequence starts, and add the month number to it. The same with the days.

The implementations of the base <UIPickerViewDataSource> methods are fairly trivial. We're displaying 3 components, and each one has 10,000 rows.

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 3;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return LARGE_NUMBER_OF_ROWS;
}

Getting what's selected at the current moment is fairly simple. I get the selected row in each component and either add 1 (in the case of NSYearCalendarUnit), or do a little mod operation to account for the repeating nature of the other calendar units.

- (NSDateComponents *)selectedDateComponents {
    NSDateComponents *c = [[NSDateComponents alloc] init];

    [c setYear:[self selectedRowInComponent:YEAR_COMPONENT] + 1];

    NSInteger monthRow = [self selectedRowInComponent:MONTH_COMPONENT];
    [c setMonth:(monthRow % maxMonthRange.length) + maxMonthRange.location];

    NSInteger dayRow = [self selectedRowInComponent:DAY_COMPONENT];
    [c setDay:(dayRow % maxDayRange.length) + maxDayRange.location];

    return [c autorelease];
}

Finally, I need some strings to show in the UI:

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    NSString *format = nil;
    NSDateComponents *c = [[NSDateComponents alloc] init];

    if (component == YEAR_COMPONENT) {
        format = @"y";
        [c setYear:row+1];
        [c setMonth:1];
        [c setDay:1];        
    } else if (component == MONTH_COMPONENT) {
        format = @"MMMM";
        [c setYear:5774];
        [c setMonth:(row % maxMonthRange.length) + maxMonthRange.location];
        [c setDay:1];
    } else if (component == DAY_COMPONENT) {
        format = @"d";
        [c setYear:5774];
        [c setMonth:1];
        [c setDay:(row % maxDayRange.length) + maxDayRange.location];
    }

    NSDate *d = [hebrewCalendar dateFromComponents:c];
    [c release];

    [formatter setDateFormat:format];

    NSString *title = [formatter stringFromDate:d];

    return title;
}

@end

This is where things are a little bit more complicated. Unfortunately for us, NSDateFormatter can only format things when given an actual NSDate. I can't just say "here's a 6" and hope to get back "Adar I". Thus, I have to construct an artificial date that has the value I want in the unit I care about.

In the case of years, that's pretty simple. Just create a date components for that year on Tishri 1, and I'm good.

For months, I have to make sure that the year is a leap year. By doing so, I can guarantee that the month names will always be "Adar I" and "Adar II", regardless of whether the current year happens to be a leap year or not.

For days, I picked an arbitrary year, because every Tishri has 30 days (and no month in the Hebrew calendar has more than 30 days).

Once we've built the appropriate date components object, we can quickly turn it into an NSDate with our hebrewCalendar ivar, set the format string on the date formatter to only be producing strings for the unit we care about, and generate a string.

Assuming you've done all this correctly, you'll end up with this:

custom hebrew date picker


Some notes:

  • I've left out the implementation of -pickerView:didSelectRow:inComponent:. It's up to you to figure out how you want to notify that the selected date changed.

  • This doesn't handle graying out invalid dates. For example, you might want to consider graying out "Adar I" if the currently selected year isn't a leap year. This would require using -pickerView:viewForRow:inComponent:reusingView: instead of the titleForRow: method.

  • UIDatePicker will highlight the current date in blue. Again, you'd have to return a custom view instead of a string to do that.

  • Your date picker will have a blackish bezel, because it is a UIPickerView. Only UIDatePickers get the blueish one.

  • The components of the picker view will span its entire width. If you want things to fit more naturally, you'll have to override -pickerView:widthForComponent: to return a sane value for the appropriate component. This could involve hard coding a value or generating all the strings, sizing them each, and picking the largest one (plus some padding).

  • As noted previously, this always displays things in Month-Day-Year order. Making this dynamic to the current locale would be a little bit trickier. You'd have to get a @"d MMMM y" string localized to the current locale (hint: look at the class methods on NSDateFormatter for that), and then parse it to figure out what the order is.

NSLog outputs unicode characters as garbage when debugging on the iPhone

11 votes

EDIT: NSLog output works well in the simulator, but doesn't work when connected to a real device. And it seems that it is a bug — http://openradar.appspot.com/11148883. Also it happens that it is related to the LLDB, switching Xcode to GDB resolves the problem. Either it's possible to JetBrain's AppCode, which works well with the LLDB.


I have a bunch of unicode strings in the application, and if I try to output any of those strings using something like NSLog(@"%@", aString) then all the ASCII characters in the string will be printed fine but all the cyrillic letters will be messed up, so instead of

newLocation: coordinate:60.019584,30.284954 'Удельная'

I'm getting:

newLocation: coordinate:60.019584,30.284954 '–ü–æ–∫–ª–æ–Ω–Ω–æ–≥–æ—Ä—Å–∫–∞—è'

And that's quite hard to do any debugging with that kind of output. And because that app is targeted for the Russian market only I can't just change locale and use English strings.

So I wonder if there any way to make NSLog work well with unicode characters? And I'm looking only for some kind of one-liner solution, I know that there are some ways to write half a page of code and output unicode chars, but I'm looking for something shorter. Ideally I'm looking for some method of NSString that will make it all work. e.g.

NSLog(@"%@", [aString someThingThatMakesUnicodeWorkWithXcodeConsole]);

As far as I know it is relevant to NSLog() and LLDB on some Xcode versions. Have a try with these solutions:

  1. Check log in Xcode Organizer >> Devices >> your device >> Console.
  2. Use GDB as your debugger instead of LLDB if you are using the latter one.
  3. Upgrade to Xcode 4.3.2. Some people say it solved this issue, but I haven't confirmed this myself.

How to open a native iOS app from a web app

11 votes

I'm trying to create an iOS web app, to be opened in full-screen mode from a link saved on the Home Screen. The web app needs to open a specific native app. I have already registered the url scheme for the native app, and verified that it works correctly - I can open the native app by typing the scheme directly into my safari address bar, for instance. I can also open it from other applications using the +openURL: method of UIApplication. I would like to also open it with certain arguments from a native web app that can be added to the home screen.

What I'm trying to do is use javascript like so inside the native app:

window.location = "myapp://myparam";

When using this code inside the web app I get an alert "Cannot Open myWebAppName - myWebAppName could not be opened. The error was "This URL can't be shown".". This same javascript when executed within Safari works correctly. I get the same result using window.location.replace("myapp://myparam").

The html for the web app is:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>untitled</title>
    <meta name="generator" content="TextMate http://macromates.com/">
    <meta name="author" content="Carl Veazey">
    <!-- Date: 2012-04-19 -->
    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta names="apple-mobile-web-app-status-bar-style" content="black-translucent" />
</head>
<body>
    <script type="text/javascript" charset="utf-8">
        if (window.navigator.userAgent.indexOf('iPhone') != -1) {
            if (window.navigator.standalone == true) {
                window.location = "myapp://myparam";
            } else {
                document.write("please save this to your home screen");
        };} else {
                alert("Not iPhone!");
                document.location.href = 'please-open-from-an-iphone.html';
        };
    </script>
</body>
</html>

What am I doing wrong here? I'm pretty inexperienced with javascript and mobile web so I suspect I'm just missing something obvious. Thanks!

tl;dr I have an app with a correctly functioning URL scheme that I'd like to launch from a web app stored on the home screen, and the normal javascript redirect methods don't seem to work.

While it's possible there's another way with some javascript I'm not aware of, to the best of my knowledge at this time it is not possible to do so :(

Delphi XE2 app doesn't respond on IPAD

10 votes


I try to use Delphi XE2 for develop for an iPad 3 professional application.
The configuration is ok.
I work on a MacBook Pro with Windows 7 on VMWare.
I create an HD app for iOS.
I put a timer, two labels and a button.
The timer refresh the time on label1 and the "on_click" event of the button change the label of label2.
It's compiled with Delphi.
I export to Xcode in the shared folder.
I launch Xcode and try with the iPad simulator.
All is ok.
I try to run on a real iPad, the application is launched but:
* the form is shown as a very small panel
* the timer seems not working but if i rotate the IPAD, the screen is refreshed and the time is shown.
* MY MAIN PROBLEM: if i tap on the button nothing happens.
I tried a lot of events, all works in delphi, in iPad simulator but not on the real iPad.

So, what the good options for fire events on a iPad ? Thanks in advance

EDIT:
I try to run the same application on a iPhone 4S and it works fine!
The trouble is only on iPad 3. I haven't no iPad 1 or 2 for testing.

I finally solved this trouble.
The installation of Delphi XE2 update 4 need to have XCODE 4.2 installed.
If you don't have it you can't install FireMonkey-iOS.dmg.
You must have too the latest version of XCODE 4.3.2 in parallels.
Of course, BE CAREFUL, of what version of Xcode is launched!
It easily to understand that for used the new IPAD (3) you must used at least Xcode 4.3.2.
If you use Xcode 4.2, your project will be compiled, running but now responding, because it don't know about iPad 3.
Enjoy.

Purchase of this item is not currently available

10 votes

I have tried to purchase something in the ios game of sandbox environment,use Test User,after i entered the appid and passward,i got the message:

" Purchase of this item is not currently available. This item is being modified. Please try again later "

it works fine yesterday,i change nothing,but it can't work today. I'm unsure what the cause is.

many thanks.

There is two possible ways this error to be happened.

  1. If you changed the details of the product ID on the IAP, It may take some more time to process

  2. Coding and user control is up to you click buy on the alert after clicking "Buy" asking apple ID, password and further are in the control of StoreKit framework that is It may be a server problem while processing on the App Store.

How do I make sure my view is oriented properly after full screen video?

9 votes

I've got a universal ipad/iphone app that allows the user to watch a video, which they can then expand into full screen mode.

I have implemented (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration, and in that method I perform various setFrame calls on my view elements depending on whether they are in landscape or portrait orientation.

That all seems to work fine in normal use, i.e. rotating back and forth works fine.

But if the user starts in portrait mode, starts a video, goes to full screen mode, turns into landscape orientation, and then the video stops -- the elements are often not resized properly. They appear to be sized still as if they are portrait mode.

If I then turn to portrait mode, and then turn back to landscape, the view resets correctly.

The strange part is, I have implemented (void)exitedFullscreen:(NSNotification*)notification and in there I print out the orientation, and it's seen correctly. I also call my code to reset the view elements based on the current orientation, and I am still having this problem.

Another related issue is sometimes when dealing with rotation, my views will end up too far up the screen, actually going under the status bar at the top of the device.

Edit Here's the latest example. I rotate to landscape mode during full screen video playback, and then when I left full screen video, you can see the issue with the navigation bar at the top of the view.

Navigation bar goes underneath status bar after rotation during full screen video.

Your ViewController might not be rotating because another controller is the first responder. What you can do to avoid this is register the view controller to the device rotation changes and implement the rotation in the selector you call when you receive such a notification.

In appDelegate:

    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

In your view controller

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:)name:UIDeviceOrientationDidChangeNotification object:nil]; 

In did rotate you can check the orientation with

    [[UIDevice currentDevice] orientation]

Saving iOS Application Scene State

9 votes

This may be impossible, but I'm trying to save the state of my application between scene transitions, but I can't figure out what to do. Currently I love the way that when you have an application running and hit the home button, you can go back to that application just where you left off, but if you transition between scenes (in a storyboard), once you get back to that scene the application state was not saved.

I only have two different scenes that need to be saved (you transition back and forth from one to the other). How can I go about saving a storyboard scenes state without taking up precise memory?

More Detailed: Here is my entire storyboard. You transition back and forth between scenes using the plus toolbar button. On the second scene the user can tap on the table view cells and a real image will fill the image view (See figure 1.2)

Figure 1.1Figure 1.1

In figure 1.2 you see what happens when you tap inside one of the many table view cells (an image view pops up.)

Figure 1.2 Figure 1.2

THE PROBLEM: When you tap a table view cell, which fills an image view (shown in figure 1.2) it works fine if you stay on that scene or even hit the iPhone home button (if you hit the iPhone home button and then reopen the app the scene's state was saved and the image view filled with a simple image still shows just like we left it), but if I transition (using the plus button) back to the first scene, and then use the plus button on the first scene to get back to the second scene the image view that I created (shown in figure 1.2) disappears and the second scene loads without saving the state and image views we filled.

EDIT: I tried using the same view controller for both scenes, but it didn't solve the problem.

UPDATE: I just found the following code (that I think stores a views state). How could I use this and is this what I've been looking for?

MyViewController *myViewController=[MyViewController alloc] initWithNibName:@"myView" bundle:nil];
[[self navigationController] pushViewController:myViewController animated:YES];
[myViewController release];

I would suggest a combination of two things: 1. Take DBD's advice and make sure that you don't continuously create new views 2. Create a shared class that is the data controller (for the golfers, so that the data is independent of the scene)

The correct way to make the segues would be to have one leading from the view controller on the left to the one on the right. However, to dismiss the one on the right you can use

-(IBAction)buttonPushed:(id)sender 
      [self dismissModalViewControllerAnimated:YES];
}

This will take you back the the view controller on the left, with the view controller on the left in its original state. The problem now is how to save the data on the right.

To do this, you can create a singleton class. Singleton classes have only one instance, so no matter how many times you go to the view controller on the right, the data will always be the same.

Singleton Class Implementation (Of a class called DataManager) - Header

@interface DataManager : NSObject {
}

+(id)initializeData;
-(id)init;
@end

Singleton Class Implementation (Of a class called DataManager) - Main

static DataManager *sharedDataManager = nil;

@implementation DataManager
+(id)initializeData {
    @synchronized(self) {
        if (sharedDataManager == nil)
            sharedDataManager = [[self alloc] init];
    }
    return sharedDataManager;
}

-(id)init { 
    if(self == [super init]) {
    } 
    return self;
}
@end

Then, inside your view controller code you can grab this instance like this

DataManager *sharedDataManager = [DataManager initializeDataManager];

This way you will have the same data no matter how many times you switch views. Also, you can better adhere to MVC programming by keeping you data and your view controllers separate. (http://en.wikipedia.org/wiki/Model–view–controller)

PhoneGap 1.4 wrapping Sencha Touch 2.X - What about performance?

8 votes

I'm building a multiplatform tablet app wrapping it with Phonegap 1.4 using just its webview, then I work my magic with the Sencha Touch 2 framework. By multiplatform I mean iOS 5.X+ and Android 3.0+ (for now).

This app is working great so far, all its features work on both systems but... On the Android tablet (Samsung GalaxyTab) its really slow. What's happening? Can I do something about it, or its just android's limit?

Thanks


*EDIT* (I'm trying to make this post somewhat useful to the comunity)

Sencha Touch, like many other Javascript Frameworks are not the best example of performance due to javascript itself.

Then Why use Senha Touch?

  • In my case: Multiplatform (iOS, Android, Windows Phone, Blackberry, Windows, Mac OSX, Linux. Sharing 80-90% of the code)

Mitigating performance issues due to lack of visual pre-process in Android systems:

  1. CSS3 heavy visual process:

    • Avoid Gradients
    • Avoid Shadows
    • Avoid Transformations and animations
  2. Good MVC practices:

    • Don't use more views and you actually showing
    • Pre-render / Pre-datafetch when possible to avoid render and data process simultaneously

Same here. I've tested many of my Sencha Touch 2 applications on Samsung GalaxyTab and the performance is really terrible. There's a fact (which maybe a part of actual reason) that, iOS does many pre-process and calculation before rendering to make it seems smoother to user's look and feel, while Android tends to render & process simultaneously on the go.

In general, it could be say that, to every cross-platform mobile apps built on Javascript, like Sencha Touch, iOS performance is significantly better than Android. However, Sencha Touch dev team is trying their best to improve this, hopefully it would be better in next releases. You could see this article about iOS & Android devices performance comparison.

http://www.sencha.com/blog/sencha-touch-2-developer-preview/

PS: While it's much relevant to the OS's limit, you can also optimize your app to make it perform better on Android devices. To my experience, the best practice is:

  • Do NOT use CSS3 too much.
  • Keep your DOM as minimal as possible.

Hope it helps.

Empty method name, what does this actually do?

8 votes

I'm currently learning myself objective-c and iOS programming and found myself stuck with non-working code due to this subtle error for an hour. Consider the following code:

@property (strong, nonatomic) NSString *name;
- (NSString *):name { return @"Some name"; }

At first glance (and for anyone new) this looks like an overridden getter for the name property. But theres a very subtle : that shouldn't be there. You get no warning/error from the compiler/parser/runtime here, so my question is what does this actually end up as?

I tried to figure a way of calling this method once I saw the error, but didn't succeed in my few attempts.

The method signature - (NSString *):name breaks down to the following:

  • - It is an instance method (versus a class method with a +).
  • (NSString *) It returns a string.
  • : If you were to speak the name of this method, it would simply be called "colon". : tells the compiler that your method accepts one parameter as well.
  • name There is a parameter called name.

When you don't specify a type, the compiler assumes you meant id, so this method actually fleshes out to be - (NSString *):(id)hello

A valid call to this method would be: [self :@"hello"].

You can do really weird things because : is a valid name for a method, and the compiler assumes id. You could, if you really wanted to, have a method called - :::. The compiler would assume you meant - (id):(id):(id):(id), a method that returns an object of type id and takes three parameters of type id. You'd call it like so: [self :@"hello" :anObject :myObject];

An error occurred uploading to the iTunes store

7 votes

Using Xcode 4.3.2.

Since yesterday, I can't upload to ITunes Store. I can't find any reasons.

Archive -> Validate... no problem.

But, it is occurred error in distribute.

enter image description here

My app is using Push.

My Xcode version is 4.3.2.

What do I do to escape this state?

Please help me... and thank you.

I had the similar issue..which i was able to fix using the solution mentioned here - An error occurred uploading to the iTunes Store - Please upgrade Java

for quick reference I am adding the solution here also

open terminal window and execute below lines of code

cd /System/Library/Frameworks/JavaVM.framework/Versions
sudo ln -s CurrentJDK /System/Library/Frameworks/JavaVM.framework/Versions/1.5
sudo ln -s CurrentJDK /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0

Lazily asking for Location Services

7 votes

As soon as I install and launch my app for the first time, it immediately asks for location services, even though startUpdatingLocation hasn't been called. I haven't even allocated a CLLocationManager instance at that point. I'd like this prompt to be done lazily after I actually make the call so the user doesn't blindly turn it off. Has this flow changed with iOS recently? I remember it used to be lazily asked. I'm using 5.1 firmware.

Thats interesting. I tested your hypothesis, that the location manager isn't even allocated and yet the iOS Location Permission dialogue comes up. I tried the following troubleshooting. After every step I ran the project on a device and I could only get the location popup to appear at the last step.

  • Start a new app
  • require location-services in info.plist
  • require gps in info.plist
  • link CoreLocation to the project
  • Import Core Location into header
  • Make and synthesise a property for a location manager
  • ask CLLocationManager for +authorizationStatus
  • ask CLLocationManager for +locationServicesEnabled
  • alloc init a CLLocationManager instance
  • tell CLLocationManager to start updating location.

Only the final step brought up a prompt. To be sure i even created a viewController that pushed a second view controller that initialised a CLLocationManager and started updating the location. The permission popup only came up when the second view controller was pushed. This is all with the latest Xcode running on 5.1.

I have shown that location services still only asks for permission at the exact moment you try and turn locations on like it has done in previous SDKs. Perhaps check your code, maybe paste some here so we can go into more detail. :)

Suitable key for NSDictionary

7 votes

Is there a way to determine if a class is suitable as a key and will work as you expect, for example I want to use NSIndexPath as a key in NSDictionary but I don't know for certain if two different NSIndexPath instances with the same integer values will always return the same hash value.

Apple's NSObject's isEqual document says:

If two objects are equal, they must have the same hash value. This last point is particularly important if you define isEqual: in a subclass and intend to put instances of that subclass into a collection. Make sure you also define hash in your subclass.

Look the following code:

NSIndexPath *indexPath1 = [NSIndexPath indexPathForRow:0 inSection:0];

NSIndexPath *indexPath2 = [NSIndexPath indexPathForRow:0 inSection:0];

NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];

NSLog(@"NSIndexPath isEqual's Result: %d", [indexPath1 isEqual:indexPath2]);
NSLog(@"NSObject isEqual's Result: %d", [obj1 isEqual:obj2]);

Output Result:

NSIndexPath isEqual's Result: 1

NSObject isEqual's Result: 0

The implementation of NSObject isEqual is that comare the address of two objects, and hash implementation is that return object's address.

NSIndexPath is inherited from NSObject, according to NSIndexPath isEqual output result, NSIndexPath's isEqual implementation should override superclass's isEqual method, and NSIndexPath also override superclass's hash method.

In attition, NSIndexPath also conform to the NSCopying protocol.

So NSIndexPath can be used as the Key class of NSDictionary.

iPhone - UIScrollView inside UIView not scrolling

7 votes

I have a UIView which contains lot of labels, buttons, textviews. But there is a certain part of that UIView that I want to make scrollable.Here is how the structure of my nib is. -UIVIew -imageView - backgroundImage -Labels -Buttons -Textviews -UIScrollView -UITextview

Are touch events getting assigned to somewhere else? Here is a code of my scrollview which I have written in viewDidLoad.I only want the textview inside UIScrollview to be scrollable

scrollView.delegate = self;    
[scrollView setCanCancelContentTouches:NO];
scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView.clipsToBounds = YES;
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.autoresizesSubviews=YES;
[scrollView setContentMode:UIViewContentModeScaleAspectFit];
[scrollView addSubview:textview]; 

Am I mising something? is my approach right?

Thanks and regards

scroll.contentSize= CGSizeMake(320,2700);// You can use contentsize according to your requirements

You have put here only scrollview code. So,I can't figure out what is wrong. Use following reference...

UIScrollView With UITextField

Hope, this will help you..

How to dispatch on main queue synchronously without a deadlock?

7 votes

I need to dispatch a block on the main queue, synchronously. I don’t know if I’m currently running on the main thread or no. The naive solution looks like this:

dispatch_sync(dispatch_get_main_queue(), block);

But if I’m currently inside of a block running on the main queue, this call creates a deadlock. (The synchronous dispatch waits for the block to finish, but the block does not even start running, since we are waiting for the current one to finish.)

The obvious next step is to check for the current queue:

if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
    block();
} else {
    dispatch_sync(dispatch_get_main_queue(), block);
}

This works, but it’s ugly. Before I at least hide it behind some custom function, isn’t there a better solution for this problem? I stress that I can’t afford to dispatch the block asynchronously – the app is in a situation where the asynchronously dispatched block would get executed “too late”.

I need to use something like this fairly regularly within my Mac and iOS applications, so I use the following helper function (originally described in this answer):

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

which you call via

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

This is pretty much the process you describe above, and I've talked to several other developers who have independently crafted something like this for themselves.

I used [NSThread isMainThread] instead of checking against dispatch_get_current_queue(), because the caveats section for that function once warned against using this for identity testing. It no longer does, so it's probably safe to do the check you use above.

Translating iOS app to unsupported/non-standard languages

6 votes

I am expanding an existing iPhone app (4.x and up) with support for more languages: Estonian, Latvian and Lithuanian.

In my iPhone and in the simulator there is no support for these languages, and I am pretty sure that no special firmware exists for them either for use in those territories.

How can I best make an app that supports them?

I have come up with two solutions which I don't really like. Neither of them allows me to have more than one language in the app, since the user cannot choose the bundled languages from the Settings.app list. This means that one version must be submitted for each language.

Option 1: Abuse the en.lproj directory

For each targeted language (lt, lv, et) I put the strings files for that language into an en.lproj directory.

Pros: Uses a well-known mechanism. The app just thinks it is running English.

Cons: Wreaks havoc on my localization tools. Its confusing for future maintainers and therefore error prone. Requires a weird build setup.

Option 2: Abuse NSUserDefaults[AppleLanguages]

The AppleLanguages object in NSUserDefaults contains a list of languages for the app to use. By setting it like this I can get the app to load for example Lithuanian from an lt.lproj directory:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"lt", nil] forKey:@"AppleLanguages"];

(For historical reasons I am already doing a slightly more involved version of this to remove a deprecated translation in some versions of the app. Otherwise older installations would pick up the lproj dir even though I no longer bundle it with the app.)

Pros: Uses properly named lproj directories. Integrates well with localization tools. Simple setup. Only requires one line in main.m to implement.

Cons: Even though the AppleLanguages key is being used by a lot of people this solution uses it to load otherwise unsupported languages, so I fear I might be skating on thin ice.

Le Questions

  • How do other apps normally support these "unsupported" languages?
  • Is there a way to support them along with the normally supported languages?
  • How do you feel about the AppleLanguages hack?

Why not adding language setting within your app then use this code (i use it in a project where the user can switch languages within the app after a requirement from my client).

It basically overwrites NSLocalizedString and uses the same file structure (en.lproj, etc.) for keeping the same language files you use when you use the "apple-way".

Give it a try!

.h file

#import <Foundation/Foundation.h>

//#undef NSLocalizedString

#define ___(key) \
[[I7I18N sharedInstance] localizedStringForKey:(key)]

#undef NSLocalizedString
#define NSLocalizedString(key,value) \
[[I7I18N sharedInstance] localizedStringForKey:(key)]

@interface I7I18N : NSObject

@property (nonatomic, retain) NSMutableDictionary *i18nTable;
+ (I7I18N *)sharedInstance;
- (NSString *)localizedStringForKey:(NSString *)key;
- (void)setLocale:(NSString *)lProjFile;

@end

.m file

#import "I7I18N.h"

static I7I18N *sharedInstance;

@implementation I7I18N
@synthesize i18nTable=_i18nTable;

+ (I7I18N *)sharedInstance {
    if(!sharedInstance) {
        sharedInstance = [[I7I18N alloc] init];
    }

    return sharedInstance;
}

- (id)init
{
    self = [super init];
    if (self) {
        self.i18nTable = [NSMutableDictionary dictionary];

        NSArray *validLocalizations = [[NSBundle mainBundle] localizations];
        [self setLocale:[validLocalizations objectAtIndex:0]];
    }
    return self;
}

- (void)setLocale:(NSString *)lProjFile {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"Localizable.strings" ofType:@"" inDirectory:[NSString stringWithFormat:@"%@.lproj",lProjFile]];
    self.i18nTable = [NSDictionary dictionaryWithContentsOfFile:path];
}

- (NSString *)localizedStringForKey:(NSString *)key {
    NSString *possibleI18NString = [self.i18nTable objectForKey:key];
    if(!possibleI18NString) {
        return key;
    }
    return possibleI18NString;

}

@end

Update 1: Do not forget to build all your view (all NSLocalizedString dependencies when switching the language with [[I7I18N sharedInstance] setLocale:@"yourlang.lproj"].