My Second Mac App

在 macOS 中我用過好幾個字典,它們都有一個共同的問題:功能太多…

我是一個 minimalist,最怕看到一大堆沒用的 features.. 所以我決定寫一隻我喜歡的字典,其特色如下:

  • 可長註 menu bar
  • login 時可 auto start
  • 用 Safari 時可以 short cut 查詢已 highlight 的字
  • UI 要簡潔


而我覺得我做到了,是很喜歡的一隻 app.

Btw, Apple 經過幾年來的不斷改善,用 Swift 和 Interface Builder 來寫 macOS App 感覺已經變得非常良好。

寫 Mac App 最開心是不用針對不同的 screen size / device 做 optimisation 和 testing,這是比寫 iOS App 爽很多的地方。

另外值得一讚的是,Apple 現在的審批速度快了很多,基本上一天便有回覆,比以往等足一星期,實在是有效率多了~



My Second Mac App

My First Mac App

其實寫 Mac Apps 這念頭在我腦海中存在已久,但它的技術門檻比 iOS app 高許多,加上不能放廣告,所以一直都提不起勁去寫…

Apple 今年在技術上有很大的變動(Swift, Storyboard… etc),加快了 Mac app 的開發流程,亦使開發過程變得愉快一些。客觀條件成熟,所以決心一試~

寫這 app 是因為我是個執筆忘字很嚴重的人,用同音字去查字,幾乎是我每日的指定動作。



把這些問題加起來,我決定用一個 app 去解決它,於是乎這個 Chinese Helper 便誕生了~






把它做成 Menu Bar app 並設定為 auto start,一開機它便在我的 desktop 了,很方便~

希望其他用戶亦覺得它方便吧 ^^

My First Mac App

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!!

UI Preparation of Universal Apps

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.

Play Audio While the Silent Switch is ON

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.

Mute Checking in iOS7

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.

Changing UISearchBar Button Color

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.

Tab Bar Background Color and Button Tint Color