Unified Contacts

29 August, 2015

advance(startIndex, n) is change to Index.advancedBy(n)

In Xcode 7 Beta 6 global function advance(startIndex, n) is no longer available. You must use Index.advancedBy(n) to get the same result. advance(startIndex, n) is very useful in substringing. Since you cannot pass an Int to the Index type in substring function.

Prior to Xcode 7 Beta 6
var str = "abc"
firstChar = str.substringToIndex(advance(str.startIndex, 1))   // a

Xcode 7 Beta6
var str = "abc"
firstChar = str.substringToIndex(str.startIndex.advancedBy(1))  // a

15 April, 2015

Default Behavior of UIPopoverPresentationController Changed in iOS 8.3

I have working with an adaptive app that can run on all size class. Before updating to iOS 8.3, the popover behavior is the same in iPad and iPhone 6 Plus landscape mode. However, after updating to iOS 8.3, the popover in iPhone 6 Plus has changed to FormSheet. This is because the popover now not only check the horizontal size class, but also the vertical size class. Popover will present normally in vertical regular like iPad, and present as FormSheet in vertical compact. So how can we fix it?

Below is the old method to disable the adaptivity of popover in horizontal compact. But it not enough in iOS 8.3.

In the demo of WWDC 2014 Session 214 "View Controller Advancements in iOS 8", Bruce explained a trick that let you present popover in horizontal compact environment (e.g. iPhone). Without this trick, the popover will be presented in modal. Let take a look what is the trick.

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
    return UIModalPresentationStyle.None
}

This function in UIAdaptivePresentationControllerDelegate can modify the UIModalPresentationSytle when the device is changed from horizontal regular to horizontal compact. By return .None, popover will no longer adaptive to horizontal compact and will presented in popover just like that in horizontal regular.

Okay, we now know that popover check now check both horizontal and vertical size class. Therefore, we need to use a new funcation. Apple introduced a new function in iOS 8.3 which let you determine the UIModalPresentationStyle by the TraitCollection.

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!, traitCollection: UITraitCollection!) -> UIModalPresentationStyle {
    return UIModalPresentationStyle.None

}

This new function can be used to replace the old function. I have disabled the adaptivity of popover in whatever size class and everything works just like in iOS 8.2.

Reference: iOS 8.3 API Diffs

12 April, 2015

Problem When Appending Tuple into Array

I've got the following error message when trying to append a tuple into an array.

Missing argument for parameter #2 in call

Here is my tuple
    (String, UInt64, NSDate?)

I get the tuple from another function and then append to the array

var fileRecords: [(String, UInt64, NSDate?)]?

var tuple = createArchive()
self.records!.append(tuple!)    <- error here

However, if I assign the tuple to a constant first and then append that constant to array. It is work.

var tuple = createArchive()
let aTuple = tuple!
self.records!.append(aTuple)    <- Okay

15 March, 2015

Element Name in Tuple only Local Significant

Tuple is a new data type in Swift. It can group multiple values of different type of data into a single tuple. It is useful when you want to return multiple values in a function.

There is an example in the Swift document from Apple, you can give elements a name when you create the tuple.

let http200Status = (statusCode: 200, description: "OK")

println("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"
println("The status message is \(http200Status.description)")
// prints "The status message is OK"


The element name makes the code more readable and meaningful. However, the name can only be used within the scope. If you get a tuple that returned from a function. The element name will be removed. The element can be gotten by index number of the element.

func getHttp200Status() -> (Int, String) {
    let http200Status = (statusCode: 200, description: "OK")

    return http200Status
}

var status = getHttp200Status()
println("The status code is \(http200Status.statusCode)")
// '(Int, String)' does not have a member named 'statusCode'
println("The status code is \(http200Status.0)")


Elements are numbered from 0 and autocomplete will tell you what is inside the tuple.



Tuple是Swift的新資料類型. 它可以把多個不同資料類型的值組合在一個tuple內. 當你想function回傳多個值時, 這個會很有用.

這裡有一個範例在Apple的Swift文件. 當創建一個tuple時, 你可以給予每一個元素一個名字.

let http200Status = (statusCode: 200, description: "OK")

println("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"
println("The status message is \(http200Status.description)")
// prints "The status message is OK"


這個名字能令你的code可讀性更高及更有意思. 不過, 這個名字只可以在scope內使用. 如果你由一個function取得一個tuple, 元素的名字會被移除. 而元素仍能以索引數字取得.

func getHttp200Status() -> (Int, String) {
    let http200Status = (statusCode: 200, description: "OK")

    return http200Status
}

var status = getHttp200Status()
println("The status code is \(http200Status.statusCode)")
// '(Int, String)' does not have a member named 'statusCode'
println("The status code is \(http200Status.0)")


元素索引由0開始, 自動完成會告訴你有甚麼元素在tuple裡.

05 October, 2014

Using Storyboard as Launch Screen

In iOS8, you can use a storyboard or xib file as the launch screen. Therefore, you can design a adaptive launch screen to fit in all devices that with different screen size. This save development time and no need to manage many launch images any more. If you start a new project, a LaunchScreen.xib is created for you by default. But you can still continue to use static launch image for iPhone 6 and 6 Plus. The resolution are listed below.

For iPhone 6:
 - 750 x 1334 (@2x)

For iPhone 6 Plus:
 - 1242 x 2208 (@3x)




在iOS8, 你可以使用storyboard或xib檔作為launch screen. 你可以計設一個適應性launch screen去配合所有不同呎吋的裝置. 這節省了開發時間及不需再管理很多launch image. 如果你新增一個project, 預設有一個xib作為launch screen. 但你還是可以繼續使用靜態launch image給iPhone 6及6 Plus. 以下是他們的解像度.

For iPhone 6:
 - 750 x 1334 (@2x)

For iPhone 6 Plus:
 - 1242 x 2208 (@3x)

16 September, 2014

Using Image Set with Universal Storyboard

In universal storyboard, you can define a UIImageView for size classes of Any Any (Width Height). Therefore, code for this UIImageView will be effective in both iPad and iPhone. That's why universal storyboard is powerful and convenient. But sometimes, the image size for iPad is different from that for iPhone. In this case, image set can help you.

1. Go to "Images.xcassets" in your project. Click "+" then choose "New Image Set".


2. Rename the new image set to whatever you want.


3. In this example, I prepared 2 sets of image. The green set is for regular size (300x300) and the blue set is for compact size (150x150). By default, there is one set for any any. Now, we are going to add another set for "compact width any height". On the right hand side, change the width to "Any & Compact". Then drag the green set to any any, and drag the blue set to compact any.


4. Back to storyboard, add a UIImageView and set the source as the name of image set we just created.


5. One important thing is we have to give the position constraint to the UIImageView, otherwise it won't resize based on the image for compact size. Here, we add 2 constraints that align horizontal and vertical center to superview.


6. Let's preview before we actually run. Enable the assistant editor. Click the root of the path then choose Main.storyboard in the Preview submenu.


7. Add both iPad and iPhone to the preview by clicking the "+" on the bottom left of the assistant editor.


8. Cool!! Now we can see iOS select the correct image according to the size class.


9. Not only in preview, when you change the size class in storyboard. The image is also changed based on the size class.


This help developer to reduce number of lines, and make the universal app more consistent.



在universal storyboard, 你可以定義UIImageView的size class為Any Any (Width Height). 這樣關於UIImageView的程式碼會在iPad及iPhone都有效. 這就是為何universal storyboard如此強大. 但有時候圖片尺吋在iPad和iPhone上是不一樣的. 這個情況下image set可以幫到你.
1. 在project裡, 選"Images.xcassets". 點選"+"後選"New Image Set".


2. 為剛新增的image set改名


3. 在這範例, 我準備了2組圖片. 綠色是給regular size (300x300) 而藍色是給compact size (150x150). 預設, 只有一組any any. 現在我們要新增一組給"compact width any height". 在右手邊, 把width改成"Any & Compact". 然後把綠色一組拉到any any, 把藍色一組拉到compact any.


4. 回到storyboard, 新增一個UIImageView及把來源設定為剛剛創建的image set.


5. 一個很重要的是我們要給UIImageView位置constraint, 否則它不會按compact size的圖片改變尺吋. 這裡我們加2個contraint, 分別是對superview水平置中及垂直置中.


6. 運行前先來預覧, 開啟assistant editor. 點路徑的最頂層. 然後在Preview子選單選擇Main.storyboard.


7. 新增iPad及iPhone到preview, 在assistant editor點左下角的"+".

8. Cool!! 現在我們可以看到iOS跟據size class撰擇正確的圖片.


9. 不單的preview, 當你把storyboard的size class更改. 圖片亦會跟據size class改變.


這幫助開發者減少得多程式碼, 還令universal app便一致.

12 September, 2014

Size Classes for iPhone 6 Plus

I talked about Unified Storyboard before. On that time, many other developers was discussing about a unknown combination, regular width + compact height. And we know it today, this is iPhone 6 Plus.


Apple has updated the online document of "What's New in iOS". At this moment, iPhone 6 Plus is the only iOS device which has this size classes. And iPhone 6 Plus also is the only device support @3x image. When developing app for iPhone 6 Plus, you have to design app for 2208 x 1242 resolution. That's mean the @1x development environment is 736 x 414. And when iPhone 6 Plus runs the app, it will virtually downsize to 1920 x 1080.


iPad Portrait
iPad Landscape
iPhone Portrait
iPhone Landscape
iPhone 6 Plus Landscape



我在較早之前討論過 Unified Storyboard . 那個時候, 很多開發者都已經在討論一個未知的組合, regular width + compact height. 今天我們知道了, 這就是iPhone 6 Plus.



Apple已經更新了網上的文件"What's New in iOS". 現在, iPhone 6 Plus是惟一的iOS裝置擁有這種size classes. 還有iPhone 6 Plus也是惟一支援@3x的圖象. 當開發iPhone 6 Plus的app時, 你必需用2208 x 1242解像度. 這表示@1x開發環境是736 x 414. 當app在iPhone 6 Plus上運行時, 會自動向下調整至1920 x 1080.


iPad Portrait
iPad Landscape
iPhone Portrait
iPhone Landscape
iPhone 6 Plus Landscape