Best iphone questions in September 2010

Strange issue after upgrading to iOS 4.1 SDK

16 votes

I've upgraded the iOS SDK to the newly released 4.1 and now I have the following error while building my app:

/DeveloperBeta/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.1.sdk/System/Library/Frameworks/CoreGraphics.framework/Headers/CGPDFContext.h:60:23     

/DeveloperBeta/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.1.sdk/System/Library/Frameworks/CoreGraphics.framework/Headers/CGPDFContext.h:60:23: error: expected function body after function declarator

Looking at that line in the .h file I see the following:

CG_EXTERN void CGPDFContextAddDocumentMetadata(CGContextRef context, CFDataRef metadata) CG_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_0);

The really interesting thing is the define __MAC_10_7 which appears not to be defined anywhere. I get this error when pre-compiling the .pch file of my app.

What's curious is that a simple hello world app compiles.

Any idea what can I do?

Thanks in advance!

Stelian

This is a known bug with the iOS 4.1 SDK and building using LLVM for the iPhone Simulator. You can read all about it in this thread on Apple's Developer Forums.

The recommended solution is to add the following to Other C Flags in your project's build settings: -D__IPHONE_OS_VERSION_MIN_REQUIRED=040100 where you replace 040100 with your deployment target version (030000 for 3.0, for example).

Best practice for setting up an automated build server for iphone apps?

13 votes

I'm looking to setup an automated nightly build server for our iphone apps, and looking for advice on what works and what doesn't.

Basically, something that at least nightly runs all the unit tests and publishes a new adhoc build to our internal website.

All the developers use laptops (which'll be off overnight), so I'm considering getting a dedicated Mac Mini to do this.

I'm not sure if I should get standard Mac OS X or the server edition.

At least for the first attempt, I'm considering just using a simple shell script run from a crontab to do the actual work. In the future a full continuous integration server (hudson etc) would be good.

I've already found a few articles through searching, though they're quite brief:

http://nachbaur.com/blog/how-to-automate-your-iphone-app-builds-with-hudson

http://blog.jeffreyfredrick.com/2008/11/27/continuous-integration-for-iphonexcode-projects/

and also this stackoverflow question has some useful software info (though it's two years old now):

http://stackoverflow.com/questions/212999/continuous-integration-for-xcode-projects

Any guidance people can give on how they've setup a build server and any potential issues would be greatly appreciated.

Thanks!

Joseph

Hudson is really not hard to set up; it's what we use internally. We don't just run iphone builds from it -- in fact, there's only only one lone mac mini set up for iphone builds, and it's a relatively recent addition. We've had a half-dozen other slaves on it for other different platforms for some time.

You can play with it through the "Test Drive" link on the Meet Hudson page to get a feel for how easy it is to set up. (This is one of the things that sold me on it; it was really easy to get started with, but still configurable, extensible, and powerful enough to keep us expanding over the last few years. It replaced a really kludgy pile of hand-rolled scripts and programs that, despite being the author of, I was very happy to see laid to rest.)

We have the hudson backend running on a beefy Mac OSX server, but there's no reason you couldn't run it pretty much anywhere (linux, windows, mac).

As for configuring it for building -- it's about 6 lines of shell script in the project configuration, mostly calling xcodebuild and passing it -project and -configuration arguments.

Example:

cd ${WORKSPACE}/Engineering/

set -e
set -v

xcodebuild -project foo.xcodeproj -alltargets -configuration Distribution clean
xcodebuild -project foo.xcodeproj -alltargets -configuration Release clean
xcodebuild -project foo.xcodeproj -alltargets -configuration Debug clean

xcodebuild -project foo.xcodeproj -alltargets -configuration Distribution
xcodebuild -project foo.xcodeproj -alltargets -configuration Release
xcodebuild -project foo.xcodeproj -alltargets -configuration Debug

We haven't set up the slave to run as a service yet -- this is on the TODO list. For now we just launch it via JNLP whenever we reboot the mini it's on.

Repository is SVN, and the hudson master takes care of remembering the https auth info for us.

We actively use the Email-ext plugin, and have a build timeout plugin and an audit trail plugin since there are a lot of other people using the system and some of the builds are not well-behaved. We've experimented briefly with the Warnings plugin and Static Code Analysis plugins as well, need to get those used on more projects (we usually have warnings as errors in builds, but we do use PC-Lint and other tools on some projects; having output aggregated and tracked here is very nice). Finally the all-important Chuck Norris and Emotional Hudson plugins.

We're currently not running unit tests (shame!) on any of the iphone builds, and we just use the ordinary "Archive the Artifacts" functionality built into hudson for storing builds. These can be accessed via authorized users via the hudson web interface. I've no doubt that it would not be hard for you to run your unit tests within the framework.

</fanboy>

Our only real issues have had to do with AFP and SMB on the mac mini -- nothing to do with hudson at all, more just our internal network infrastructure. And the mini is a bit slow for my tastes -- we usually run pretty beefy build slaves on the theory that quick autobuild turnaround is a good thing. The mini may be gifted an SSD for this reason at some point.

Unit Testing broken in iOS 4.1 SDK?

10 votes

After upgrading to the 4.1 iOS SDK my unit test bundles always return with the following two errors and one warning:

An internal error occurred when handling command output: -[XCBuildLogCommandInvocationSection setTestsPassedString:]: unrecognized selector sent to instance
An internal error occurred when handling command output: -[XCBuildLogCommandInvocationSectionRecorder endmarker:]: unrecognized selector sent to instance
Run unit tests for architecture 'i386' (GC OFF) did not finish

I get these errors despite it reporting that all X out of X tests passed. I read about a similar bug in an older version of XCode that also occurred immediately after it was released (something about a bug in a regular expression for time) that could be mitigated by setting your time zone to Pacific Time, but I can't say for sure it's a regression to there specifically.

Is anyone else having this problem?

There is a fix available on the apple dev forums,

https://devforums.apple.com/thread/68687

it's a date parsing problem. The fix is quick, it requires including one .m added to your test bundle. The fix is available above or from:

http://gist.github.com/586296

Twitter-esque UITabBarController?

9 votes

I'd like to make an app that uses a UITabBarController that is similar to the one in the Twitter app for iPhone and iPod touch. (Blue light for unread and sliding arrow for switching between content views).

Is there an easy way to do this? Any open source code?

I would create a subclass of the standard UITabBarController and add a couple of subviews for the blue light and the sliding arrow. Shouldn't be too hard to accomplish.

Edit: Here's an idea of some of the stuff that should go in your subclass. I don't even know if this code will compile.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
        super.delegate = self;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    //Create blueLight
    UIImage* img = [UIImage imageNamed:@"blue_light.png"]
    self.blueLight = [[UIImageView alloc] initWithImage:img];
    self.blueLight.center = CGPointMake(320, 460); //I'm using arbitrary numbers here, position it correctly
    [self.view addSubview:self.blueLight];
    [self.blueLight release];

    //Create arrow, similar code.
}

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
    //Put code here that moves (animates?) the blue light and the arrow


    //Tell your delegate, what just happened.
    if([myDelegate respondsToSelector:@selector(tabBarController:didSelectViewController:)]){
        [myDelegate tabBarController:self didSelectViewController:viewController]
    }
}

HTML5 inline video on iPhone vs iPad/Browser

9 votes

I've created an HTML5 video player (very simple) that works perfectly on the iPad and the browser.

However, when I open it on the iPhone, I only get a play button which, when pressed, opens the native video player on a new window, on top of all my stuff.

That means I lose access to my custom controls and time tracking (written in Javascript), since the video is now running isolated.

Is there any way to override Apple's control of HTML5 video on the iphone and get it working like on the ipad?

Cheers

Right,

I was going nowhere with this and filed a bug with Apple.

After a couple of weeks they got back to me saying, very simply, that I should add "webkit-playsinline" to the video tag on the HTML, as well as adding the "allowsInlineMediaPlayback" property on the UIWebView.

So in the end, this is what it looks like:

HTML

<video id="player" width="480" height="320" webkit-playsinline>

Obj-C

webview.allowsInlineMediaPlayback = YES;

And all works just fine :)

Hope this helps someone, as it's virtually undocumented and the only place I could find a reference to "webkit-playsinline" was in the iAds reference, where it says: "iAds JS only".

How to speed up TDD flow with iOS and Objective-C/Xcode

9 votes

Have been searching for experiences on TDD with Objective-C and iOS development.
Previous post about "string calculator"-kata in Objective-C was useful (thanks).

But it would be nice to learn even more fluent iPhone-TDD.

Do you have some experience of how to use UISpec (based on Rspec), iCuke (based on cucumber) or similar tools?

And if you also have got the flow going with autotesting (autoiphonetest.rb) like Paul did in his his blog, it would be very interesting feedback.

Here is a good post: Test Driven Development in Objective-C with MacRuby

I found the following screencast pretty useful http://vimeo.com/9394596 to get started.

360° panorama librarys for ios

9 votes

are there any libraries or classes out there to show a 360 degree panorama on the iphone?

i found this here: http://code.google.com/p/panoramagl/ but its not up to date and only for old versions of ios.

i'm thankfull for any link that helps me to create a 360 degree panorama view on ios.

thanks!

I've recently found KRPano and since it's not listed here..

Categories/Interfaces From Static Libs Not Autocompleting?

9 votes

Thanks to this post and the now built-in static library template, I am able to put some of the pieces of my project elsewhere. Everything compiles with no warnings and runs fine. However, I used to get autocomplete for the categories in my import statement. This is no longer happening. How can I get autocomplete for categories in a static library?

The problem is that XCode doesn't know where to look for your header files.

To fix this, do the following - In the "User Header Search Paths" of your app's target, point to the location of your category header. That tells XCode where to look for it, and autocomplete should work. In case of multiple targets, you could fix it for each target or at the project's settings.

How can I find out if the iPhone user currently has a passcode set and encryption enabled?

9 votes

I'm writing an iPhone application that requires its data to be encrypted. I've learned how to turn on encryption for files by setting the NSFileProtectionComplete attribute. I also know how to check the iPhone version to make sure they are running iOS 4.0 or better.

What I've realized though, that if the user has not chosen a passcode and has not specifically enabled data protection on the Settings > General > Passcade Lock screen then the data isn't actually protected at all.

I'd like to pop up a warning and tell the user that they must enable a passcode and turn on data protection (which requires a backup and restore on pre-4 iPhones), and then exit the application if they do not have a passcode and data protection enabled. I can't figure out anyway to find out the state of these settings though. All of the APIs I've found, such as "protectedDataAvailable" in UIApplication all pass with success if data protection is disabled.

There is no API to access the user's passcode settings. Not the answer you're looking for, but there it is.

You could file a bug/enhancement-request with Apple.

http://bugreporter.apple.com

Handling applicationDidBecomeActive

8 votes

I have the UIApplicationDelegate protocol in my main AppDelegate.m class, with the applicationDidBecomeActive method defined.

I want to call a method when the application returns from the background, but the method is in another view controller. How can I check which view controller is currently showing in the applicationDidBecomeActive method and then make a call to a method within that controller?

Any class in your application can become an "observer" for different notifications in the application. When you create (or load) your view controller, you'll want to register it as an observer for the UIApplicationDidBecomeActiveNotification and specify which method that you want call when that notification gets sent your your application.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(someMethod:) name: UIApplicationDidBecomeActiveNotification object:nil];

Don't forget to clean up after yourself! In dealloc, remember to remove yourself as the observer:

[[NSNotificationCenter defaultCenter] removeObserver:self];

You can learn more about the Notification Center here.

Objective C parse hex string to integer

8 votes

I would like to know how to parse a hex string, representing a number, in objective c. I am willing to use both an objective, or a C based method, either is fine.

example:

#01FFFFAB

should parse into the integer: 33554347

Any help would be appreciated!

Joshua Weinberg's answer is mostly correct, however the Ox prefix is optional when scanning hexadecimal integers. If you have a string in the format #01FFFFAB, you can still use an NSScanner, but you can simply skip the first character.

unsigned result = 0;
NSScanner *scanner = [NSScanner scannerWithString:@"#01FFFFAB"];

[scanner setScanLocation:1]; // bypass '#' character
[scanner scanHexInt:&result];

Install Xcode 3.2.4, get "Base SDK Missing"

8 votes

When you update Xcode to 3.2.4, your previously working Xcode iOS project gives you the message "Base SDK Missing". Something like this:

Ruh-roh!

What gives? How to fix?

The Problem

iOS SDK 4.0 is gone in this release, replaced by iOS SDK 4.1. Details in "The Explanation" below. (NB: this repeats some information in my previous Q/A regarding 3.2.3).

The Fix

To fix this specific problem, you need to reset the Base SDK for your target(s), etc. You can either do that for the entire project (most folks), or for each target and/or configuration which applies. Rarely, you might need to do both.

Edit Project Settings

  1. Load your project
  2. From the menu, select Project > Edit Project Settings...
  3. Click the "Build" tab.
  4. Under "Configurations" select "All Configurations".
  5. Go to the Change Base SDK section below.

Edit Specific Target/Configuration Settings

  1. Load your project
  2. From the menu, select Project > Active Target > "YourTarget"
  3. From the menu, select Project > Edit Active Target "YourTarget"
  4. Click the "Build" tab.
  5. Under "Configurations" select "All Configurations", or the Configuration you want.
  6. Go to the Change Base SDK section below.

Change Base SDK

  1. Under Architecture > Base SDK, choose one of the available device options: iOS Device 3.2 or iOS Device 4.1. If you are iPhone-only, 4.1 is the way to go.
  2. If you want to target previous iOS versions, then in that same window, under Deployment > iPhone OS Deployment Target, select the lowest version you want to support. Note that support for 2.x versions through the app store is deprecated.

The Explanation

iOS SDK 4.0 is gone in this release, replaced by iOS SDK 4.1. This is very similar with what happened with the 3.2.3 upgrade, which I covered in detail here. You can still use 3.2.4 to target iOS versions down to 3.0, but AFAIK, you must compile against the 4.1 SDK with Xcode 3.2.4. If this statement confuses you, study the following sentence closely:

The iOS SDK you compile against -- the "Base SDK" -- does not downward limit what iOS versions you can support.

In fact, there are two different settings, cf. Change Base SDK above. If you remain confused, take heart, and read my detailed post on the previous switchover, which is itself chockablock with links to good stuff.

AAC/MP3 Licensing

8 votes

If I use the audio decoding libraries included in iPhoneOS (ex. AVAudioPlayer). Do I still have to pay for a license from Thomson, or VIA Licensing to legally decode AAC or MP3 files in my app?

Edit:

I was unable to find an exact answer from ADC, and am also curious about audio library's on other platforms (Windows, Mac, Android...) So I am adding a bounty to this question.

I'm afraid the only definitive answer would come from a court of law. The patent-holders will cheerfully answer "yes" to almost any question that begins with "should I be paying royalties," and asking anyone else is more or less a request for legal advice.

I also suspect very strongly that Apple has not obtained a blanket AAC/MP3 patent license on behalf of all iOS developers – I think we'd have heard about that.

Why do people always use reassignment for instance variables in objective-C (namely iphone)?

8 votes

I always see example code where in the viewDidLoad method, instead of saying, for example

someInstanceVar = [[Classname alloc] init];

they always go

Classname *tempVar = [[Classname alloc] init];
someInstanceVar = tempVar;
[tempVar release];

Why is this? Isn't it the exact same thing, just longer?

The short answer: This pattern shows up all the time in iPhone code because it is considered the best way to create a new object and assign it to a member variable while still respecting all of the memory management rules and invoking the appropriate side effects (if any) while also avoiding the use of autorelease.

Details:

Your second example would create a zombie, since var is left holding a pointer to memory that has been released. A more likely usage case looks like this:

tempVar = [[Classname alloc] init];
self.propertyVar = tempVar;
[tempVar release];

Assuming that propertyVar is a declared as copy or retain property, this code hands off ownership of the new object to the class.

Update 1: The following code is equivalent, but not recommended* on iOS, which is probably why most iPhone programs use the first pattern instead.

self.propertyVar = [[[Classname alloc] init] autorelease];

* autorelease is discouraged on iOS because it can cause problems when overused. The easiest way to be sure you never overuse it is to never use it all, so you will quite often see iOS code that uses alloc/init and release, even when autorelease would be acceptable. This is a matter of coder preference.

Update 2: This pattern looks confusing at first because of the memory management that Cocoa performs automagically behind the scenes. The key to it all is the dot notation used to set the member variable. To help illustrate, consider that the following two lines of code are identical:

self.propertyVar = value;
[self setPropertyVar:value];

When you use the dot notation, Cocoa will invoke the property accessor for the indicated member variable. If that property has been defined as a copy or retain property (and that is the only way for this pattern to work without creating a zombie), then several very important things happen:

  1. Whatever value was previously stored in propertyVar is released
  2. The new value is retained or copied
  3. Any side effects (KVC/KVO notifications, for example) are automatically handled

Does apple apply the app icon gloss effect and corner rounding on every icon?

8 votes

The iTunes Connect Developer Guide says that the developer must provide a whole bunch of different icon sizes. But I only know from the "normal" app icons (57x57, 114x114) that the device applies the gloss effect and corner rounding automatically.

But how about those other icons? How about the 512x512 iTunesArtwork.png icon? Are the effects applied automatically? Must this icon be shipped inside the bundle of the app? Is there any way to see it "live" how it finally looks, when those effects are applied?

I slightly remember that there was a tool from apple that applied those effects to an icon, so you could see them. Does anyone know more details about this?

You actually should supply 6 icons now. One for the iPhone4, iPad, normal iphones/ipod touch, and 2 smaller icons for spotlight search (one is double resolution).

I wrote a pretty cool photoshop script to take you 512px icon and convert it using photoshop into 6 smaller sizes all named correctly.

http://github.com/sponno/iPhone-Photoshop-JSX-Icon-Exporter

You will also see in the header of the file, now to update your info.plist to include all these icons.

Simple bug crashes hangs Xcode.

7 votes

I have build the simplest possible iPhone program, a window based program. I only changed the testappdelegate.h file to be the following:

#import <UIKit/UIKit.h>

@interface TestAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    int pos[10]10]; //note the error here.
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

Note the error in the 'int pos...' line.

When I try to compile this program XCode hangs (showing millions of errors). I am not asking how to fix the line, the fix is obvious.

The problem is that overwhelms XCode and I want to know why?

Update: submitted bug report Bug ID# 8406197

Update2: Sept 24. I got a response from Apple Engineering has provided the following information:

We don't plan to fix this in llvm-gcc or gcc, and it is already fixed in clang. Xcode not freaking out is tracked in a clone of this bug.

We are now closing this bug since our engineers are aware of the issue and will continue to track it offline.

The issue is that Xcode is having to ingest those millions of errors to try and figure out if there is a line of code that needs annotation.

Please file a bug via http://bugreport.apple.com/.

That you have produced such a simple test case will help the engineering team to ensure that any fixes they may be pursuing address the problem sufficiently. There are potentially two bugs here; the first for Xcode to handle such a volume of errors gracefully and the second for the compiler to not spew so much in the face of such an obvious error.

It is likely that your bug(s) will be returned as known duplicates. That only happens, though, after the engineering team has captured any unique information from your bug. That is to say, dupes are often very useful.

When filing the bug, add the bug # to your SO question. Many Apple engineers cruise SO and will click through to followup internally.

Odd Core Data error caused by over releasing?

5 votes

Occasional reader and first time question asker, so please be gentle :)

I am creating a Managed Object (Account), that is being passed into a child view controller where its being set in a property that is retained.

Account * account = [[Account alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
AddAccountViewController *childController = [[AddAccountViewController alloc] init];
childController.title = @"Account Details"; 
childController.anAccount = account;
childController.delegate = self;

[self.navigationController pushViewController:childController animated:YES];
[childController release];
[account release];

The view controller interface:

@interface AddAccountViewController : UIViewController {
}

@property (nonatomic, retain) IBOutlet UITextField * usernameTextField;
@property (nonatomic, retain) IBOutlet UITextField * passwordTextField;

@property (nonatomic, retain) Account * anAccount;
@property (nonatomic, assign) id <AddAccountDelegate> delegate;

- (IBAction)cancel:(id)sender;
- (IBAction)add:(id)sender;
- (IBAction)textFieldDone:(id)sender;
@end

So in code sample 1 I've released the account object because I am no longer interested in it in that method. As it is retained by the AddAccountViewController I have an entry in AddAccountViewController's dealloc that releases it.

However when I go to delete the object from the ManagedObjectContext the app crashes with the following (rather unclear) error:

Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData.

After much debugging & hair pulling I discovered that if I don't release account in AddAccountViewController's dealloc method the app works properly continually and doesn't appear to leak according to Instruments.

Can anyone shed any light as to whats going on? I understand from the docs on properties that those retained need to be released. What have I missed?

Update to answer Kevin's question

The code to delete the object from the ManagedObjectContext is in the RootViewController (that holding the child controller)

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the managed object for the given index path
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];

        [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

        // Save the context.
        NSError *error = nil;
        if (![context save:&error]) {
            /*
             Replace this implementation with code to handle the error appropriately.

             abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
             */
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }  
}

Firstly: It sounds like a bug on Apple's part. Core Data is calling _Unwind_Resume, which is (probably) some sort of exception unwind. Exception-unwinding exists on the phone, but (I think) uses the ARM ABI, which uses function names beginning with __cxa_. Are you running on the simulator? Which version of the SDK?

There might be an extra release floating around somewhere which is "balanced" when you remove the call to [account release];.

"Instruments doesn't show any leaks" doesn't mean there aren't any; last I checked it got confused by cycles (i.e. it wouldn't show a leak if you forgot to un-set IBOutlets in dealloc). I tested with NSMutableData * d = [NSMutableData dataWithLength:1<<20]; memcpy(d.mutableBytes, &d, 4);, but an easier test is just [[UIView alloc] initWithFrame:CGRectZero].

If you think it's a retain/release issue, I once debugged these by overriding retain/release/autorelease to call NSLog. I then added breakpoints on all of them, set them to run the command "bt", and clicked the autocontinue. Then run the thing that breaks (in my case I think it was just an extra retain), print out the log output,stick it on a whiteboard, and spend half an hour matching retains and releases.

What's the normal way of organising a header file in Objective-C?

5 votes

I do start off organising my .h files with the best intentions but somehow they get disgustingly messy.

Below is an example (which isn't that bad, but i've seen much worse!). I've tried grouping sections with #pragma mark but it seems to look even messier.

All the UILabels and UIButtons are required (as mentioned above) as they're showing data coming from a web service request so they're all required if we're using Interface Builder to design our GUI's. For example, the label might be a "weight" or "height" characteristic for a product.

Does anyone have any good advice on how to organise these in the most maintainable/readable way?

Cheers

alt text

It strikes me as possible that you have too many properties, there. I've quite literally never seen any class with this many outlets; why are you addressing every single element in your layout? And why all from one controller?

It seems as if the best solution to your problem is to consider your class and split it up into multiple classes; each controlling one aspect of your interface. You also need to make sure that you really need to address all these elements. (The UILabels and UIButtons in particular seem like strange things to have outlets for.)

Can I use NSAttributedString in Core Text in iOS?

5 votes

I'm trying to work out how to take an NSAttributedString and use it in Core Text on the iPad. I watched one of the WWDC videos (110) which has slides (but no source code) and it describes how to create an NSAttributedString, then just put it into a CTFramesetterRef:

CTFontRef helveticaBold = CTFontCreateWithName( CFSTR("Helvetica-Bold"), 24.0, NULL);

NSString* unicodeString = NSLocalizedString(@"TitleString", @"Window Title");
CGColorRef color = [UIColor blueColor].CGColor; NSNumber* underline = [NSNumber numberWithInt:kCTUnderlineStyleSingle|kCTUnderlinePatternDot];
NSDictionary* attributesDict = [NSDictionary dictionaryWithObjectsAndKeys:helveticaBold, (NSString*)kCTFontAttributeName, color, (NSString*)kCTForegroundColorAttributeName, underline, (NSString*)kCTUnderlineStyleAttributeName, nil];
NSAttributedString* stringToDraw = [[NSAttributedString alloc] initWithString:unicodeString attributes:attributesDict];

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(stringToDraw);

CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
CFRelease(framesetter);
CTFrameDraw(frame, context);
CFRelease(frame);

But when I try this, it throws the error:

Passing argument 1 of 'CTFramesetterCreateWithAttributedString' from incompatible pointer type

Are CFAttributedString and NSAttributedString 'toll-free bridged'? I've read somewhere that this is only true on OSX, but this is how Apple do it in their demo...

Thanks!

:-Joe

You can download the WWDC10 source code here. In addition, use toll-free bridging and explicitly cast your stringToDraw to a CFAttributedStringRef.