Kelvin's Blog

Life is a program you write everyday ~

UI Preparation of Universal Apps

To me, one of the most un-willing task for making universal iOS apps is to prepare the UI of iPad. Because I have to re-do everything that I’ve done for the iPhone UI, such as putting UI elements, setting UI constrains, connecting IBOutlets, setting IBActions…etc. I just boringly repeating my self…

Xcode didn’t provide any function to “migrate” an iPhone storyboard to iPad. However I saw a thread in Stack Overflow discussing how to do it manually. After experimenting myself, here summarised what I’ve done:

  1. Duplicate the iPhone storyboard and rename it Main_iPad.storyboard

  2. Right click and choose “Open As” -> “Source Code”

  3. Search for targetRuntime=”iOS.CocoaTouch”and change it to targetRuntime=”iOS.CocoaTouch.iPad”

  4. Replace <simulatedScreenMetrics key=”destination” type=”retina4″/> to <simulatedScreenMetrics key=”destination”/>

  5. Save everything and restart Xcode.

  6. In your target’s General tab, choose your newly edited storyboard in the iPad’s “Main Interface” setting.

Though we still need to adjust the size of the UI elements to fit into the iPad screen size, it saves us tremendous amount of time as all the outlets / connections / UI elements / constrains are there already!!

Play Audio While the Silent Switch is ON

I’d receive emails from time to time complaining my apps that have no sound. However, its actually caused by the user turing on the Silent Switch…

To enable audio playing even when the user turned on the silent switch. Simply put the following code in your app delegate while its launching.

AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
[audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
[audioSession setActive:YES error:nil];

The AVAudioSessionCategoryPlayback category enable your app to play audio while silent switch is on (and even the screen is locked). AVAudioSessionCategoryOptionMixWithOthers allow your audio to mix with other audio from other apps, and of course, its optional.

Mute Checking in iOS7

Up to iOS 7 there is no simple API to check whether the user switched on the mute button(silent switch) or not…

Sharkfood produced a class to constantly check if a user switched on the mute button.

The idea is playing a short(0.2 sec) muted sound file using AudioServices. If it takes longer then 0.2 sec to complete, mute button is off. Otherwise if it finished very soon, say shorter then 0.1 sec, mute button is on.

However, in most case I don’t need to keep checking mute in the run loop. I just want to know if it is muted while the user start playing some audio. So I modified it and made a class, named MuteChecker, to check it on-demand.

Also, it will call a completion block after checking. So you can put whatever logic you like for the checking result.

To use it, you just simply initialise it with a completion block.

self.muteChecker = [[MuteChecker alloc] initWithCompletionBlk:^(NSTimeInterval lapse, BOOL muted) {
    //your logic here...
}];

The “lapse” parameter return the time used for playing the checking sound.

The “muted” parameter will return YES if the time lapse is < 0.1 sec.

To start checking, call

[_muteChecker check];

and your result will be reflected by the completion block.

You can download a sample project here.

Changing UISearchBar Button Color

To change the UISearchBar button color, the simplest approach is to change the Tint color in it’s View section using storyboard.

However, it will also affect the insertion point color.

So if you want a white color button, the insertion point will disappear because is the same color as the search bar background…

To tackle this problem, we can configure the appearance of the search bar using its appearance proxy in the app delegate:

NSDictionary *attrDict = @{NSForegroundColorAttributeName: [UIColor whiteColor]};
UIBarButtonItem *buttonItemProxy = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
[buttonItemProxy setTitleTextAttributes:attrDict forState:UIControlStateNormal];

This will only change the color of the button but not the insertion point.

Note that this approach will affect all search bars in your app.

Tab Bar Background Color and Button Tint Color

To change the background color of tab bar, we can change the tab bar setting in storyboard.

However, unlike navigation bar, we cannot setting the tab bar item’s tint color inside storyboard.

We have to add some code in the app delegate, inside application:didFinishLaunchingWithOptions:, add

[[UITabBar appearance] setTintColor:[UIColor yellowColor]];

Note that this is an application-wide setting, all tab bar items will tint to the specific color.

Navigation Bar Background Color and Buttons Tint Color

To change the background color of navigation bar and the tint color of bar button items, no code is needed.

In the storyboard, click on the navigation bar of your navigation controller.

Set the Bar Tint for changing the background color. And Tint inside the View section.

Below shows how to set the background to blue and buttons to white.

Hide Status Bar

Starting from iOS 7, there are 2 ways to hide the status bar. One is to hide the status bar in all view controllers, while the other is controller specific.

  1. App-based setting

Set “View controller-based status bar appearance” in target’s info to NO.

then in the app delegate, inside application:didFinishLaunchingWithOptions:, add

[application setStatusBarStyle:UIStatusBarStyleLightContent];
  1. Controller-based setting

Set “View controller-based status bar appearance” to YES.

Inside viewDidLoad, add

[self setNeedsStatusBarAppearanceUpdate]

and then add the following function:

-(UIStatusBarStyle)preferredStatusBarStyle{ 
    return UIStatusBarStyleLightContent; 
}

NSUserDefaults KVO in iOS7

Though its not well documented, NSUserDefaults do support key-value observing in iOS7. The following code works:

- (void)viewDidLoad{
  [super viewDidLoad];
  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
  [ud addObserver:self forKeyPath:@"bar" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
  [ud setObject:@"foo" forKey:@"bar"];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
  NSLog(@"%@", change);
}

Its much more convenient then using NSUserDefaultsDidChangeNotification, since we do know exactly which key is changing.

As the hardware of mobile devices become more and more powerful, I hope the support of key binding could be actualised in near future :)

Objective C Property Attributes

Just read a post about Objective C property attributes. I wanna make some extra notes on this topic:

1. “weak” vs “assign”

We generally use weak for Objective-C objects that we don’t need to retain, e.g.

@property (weak) IBOutlet UIButton aButton;

because its always good to nil an object if it has been deallocated.

On the other hand, we could use assign for C primitives, e.g.

@property BOOL isGood;

because C primitives has nothing to do with retain count and deallocation, simply assign the value is good enough.

Note that assign is the default property attribute, we can skip it for C primitives for cleaner code.

2. The default attribute of property vs variable

There are 2 kinds of default: property and variable

For property, the default attribute is “assign”

For variable (ivar or local variable), the default ownership qualifier is “__strong”

Don’t mix up “strong” in property and “__strong” in variable. They are different things (see below).

Also remember the default property attribute is “assign”, not “strong”

3. Implicit attribute for the default

The explicit version of

@property BOOL isGood;

should be:

@property (atomic, assign, readwrite) BOOL isGood

so skipping the attributes by using the default is fine for C primitives in most cases.

However, we better explicitly state the attributes for Objective-C objects. Because we seldom want “assign” for objects.

For ivar and local variable,

NSString *str;

is equivalent to

__strong NSString *str;

so if you set it as an ivar, the compiler will automatically retain it for you when you assign a value for it. Which behave differently from not using ARC.

4. Relationship between property attributes and ownership qualifiers

Here listed their relationships according to the official documentation

  • assign implies __unsafe_unretained ownership.
  • copy implies __strong ownership, as well as the usual behavior of copy semantics on the setter.
  • retain implies __strong ownership.
  • strong implies __strong ownership.
  • unsafe_unretained implies __unsafe_unretained ownership.
  • weak implies __weak ownership.

So for property attributes in ARC, practically we can assume assign = unsafe_unretained, and strong = retain.

Simple Dice Modeling with Cheetad3D

Source files

Original tutorial by Andreu Cabré

Background music by Joseph Gilbert/Kistol

Follow

Get every new post delivered to your Inbox.