iOS Pulldown List(Dropdown List、Combobox) by Swift

最近因為工作的關係開始接觸 iOS SDK,Apple 官方文件算是頗齊全的,但相較於 Android 的手冊,還是略遜一疇(Google 大神還是比較有善~)。而本次目標是在 iOS 裡面新增一個簡單下拉式選單(Dropdown Menu、Pulldown Menu),憑著之前新增元件的經驗,我開始在 Xcode Object Library 裡搜尋下拉選單,但不管我打 Dropdown、Pulldown,還是 Combobox 就是沒有!!!一向帶給人有善印象的 Apple 咬一口,居然沒有提供下拉選單這種元件,不會要自己刻吧,崩潰。。。

還好現在第三方社群很發達,大家都很 Nice!拜了幾輪 Google 大神後,大神給了指示,原來在 iOS 裡下拉選單需要用 UITextField 這個元件來改造,然後再加上一些其他元件完成選單的內容。選單的製作 Google 大神給了我兩個答案,一個是透過修改 UITextField InputView,另一個是使用 ActionSheetPicker-3.0,以下將一一介紹:

方法一:修改 UITextField InputView(Ref.)

  1. 首先在目標 ViewController 裡面新增幾個成員變數。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class ViewController: UIViewController {
    //UITextField 的 Reference Outlet 變數
    @IBOutlet weak var textField: UITextField!
    //選單元件
    private var pickerView: UIPickerView!;
    //下拉選單的內容
    private var dataArray: NSArray!;
    。。。。。
    }

記得到 Storyboard 裡將Reference Outlet 變數與元件建立連結。

  1. 在 ViewController 的 viewDidLoad() 裡改造 UITextField,現在先將它看起來像下拉選單(新增下拉符號)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    override func viewDidLoad() {
    。。。。。
    //指定下拉符號圖片
    var img = UIImage(named: "dwn")
    self.textField.rightView = UIImageView(image: img)
    //設定圖片填滿右視圖
    self.textField.rightView!.contentMode = UIViewContentMode.ScaleAspectFit
    self.textField.rightView!.clipsToBounds = true
    //顯示下拉符號
    self.textField.rightViewMode = UITextFieldViewMode.Always
    //建立委托
    self.textField.delegate = self
    。。。。
    }
  2. 再來在 ViewController 裡新增兩個 function ,一個用來建立選單元件(使用 UIPickerView 及 Toolbar),一個用來告知 iOS 使用者已經完成選擇。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //新增選單元件
    private func showPicker() {
    //使用 UIPickerView 做為選擇器
    self.pickerView = UIPickerView()
    //顯示目前選擇指標
    self.pickerView.showSelectionIndicator = true
    //指定資料來源與委托
    self.pickerView.dataSource = self
    self.pickerView.delegate = self
    //使用 UIToolbar 建立選單元件與外觀
    var toolbar = UIToolbar()
    toolbar.barStyle = UIBarStyle.Default
    toolbar.sizeToFit()
    //新增完成選擇(Done)按鈕
    var doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: self, action: "doneClicked")
    //讓按鈕靠右對齊的元件
    var flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
    //將元件放到 toolbar 裡面
    toolbar.setItems([flexibleSpace, doneButton], animated: true)
    }
    //使用者點擊成選擇(Done)按鈕後的動作
    func doneClicked() {
    //隱藏選單
    self.textField.resignFirstResponder()
    }
  3. 最後建立使用者與 UITextField 和 UIPickerView 的互動事件,以及設定 UIPickerView 的選項資料。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    class ViewController: UIViewController, UITextFieldDelegate, UIPickerViewDataSource, UIPickerViewDelegate {
    。。。。。
    //點擊 textField 的時候出現選單
    func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
    //沒有資料的時候不顯示選單
    if self.dataArray != nil && self.dataArray.count > 0 {
    self.showPicker(textField)
    return true
    }
    return false
    }
    //不讓使用者透過建盤輸入文字
    func textField(textField: UITextField, shouldChangedCharactersInRange range: NSRange, replacementString string: String) {
    return false
    }
    //設定選單項目的列數,例如時間 (小時:分鐘) 的 pickerView 就是 return 2
    func numberOfComponentInPickerView(pickerView: UIPickerView) -> Int {
    return 1
    }
    //設定選單選項的數量
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return self.dataArray.count
    }
    //設定選單項目
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
    return self.dataArray[row] as! String
    }
    //當選單被滑動(Sliding)的時候,改變 textField 的值
    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    self.textField.text = self.dataArray[row] as! String
    }
    }

方法二:使用 ActionSheetPicker-3.0

ActionSheetPicker-3.0 這個元件是 Petr Korolev 這個好心人用 Objective-C 寫的,但我的專案目標語言是 Swift,所以要先設定套件關聯(Bridge),Petr Korolev 提供了三個方法安裝使用這個元件(CocoaPods、Carhage 和 Git Submodule),但可能我太好運了 CocoaPods 和 Carhage 的 Bridge 一直設定不起來,我只好用 Git Submodule,以下是我的作法:

  1. 設定 Git Submodule

    1
    2
    # 打開 Terminal,然後切換目錄到目標專案的根目錄,最後打上這一行
    $ git submodule add https://github.com/skywinder/ActionSheetPicker-3.0.git
  2. 用 Finder 打開專案目錄底下的 ActionSheetPicker-3.0 資料夾,然後將 /CoreActionSheetPicker/CoreActionSheetPicker.xcodeproj 拖到 Xcode 的 Project Navigator 裡面。

    ProjectSetting
  3. 用滑數點擊 Xcode Project Navigator 裡最上面的專案圖案(藍色的 Icon),右邊跳出專案設定,如下圖:

    ProjectSetting
  4. 切換專案設定的標籤到 Build Phases,在 Target Dependancy 裡面新增 CoreActionSheetPicker.framework。

    ProjectSetting
  5. 一樣在 Build Phases 裡,新增一個 New Copy Files Phase,將其重新命名為 Copy Frameworks。

    ProjectSetting

    ProjectSetting
  6. 在 Copy Frameworks 裡,選擇 Destination 為 Frameworks,然後新增 CoreActionSheetPicker.framework。

    ProjectSetting
  7. 隨便新增一個 Objective-C 檔案到專案裡面,然後 Xcode 會詢問是不是要新增 Objective-C Bridge Header,請按「是」。

    ProjectSetting

    ProjectSetting
  8. 在「專案名稱-Bridging-Header.h」裡新增下面這行:

    1
    #import "CoreActionSheetPicker/CoreActionSheetPicker.h"
  9. 在目標 ViewController 裡面新增 Reference Outlet 變數和資料容器,然後將 Reference Outlet 元件建立連結。

    1
    2
    3
    4
    5
    6
    7
    8
    class ViewController: UIViewController {
    //UITextField 的 Reference Outlet 變數
    @IBOutlet weak var textField: UITextField!
    //下拉選單的內容
    private var dataArray: NSArray!;
    。。。。。
    }
  10. 在 ViewController 的 viewDidLoad() 裡將改造 UITextField 改造成下拉選單。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    override func viewDidLoad() {
    。。。。。
    //指定下拉符號圖片
    var img = UIImage(named: "dwn")
    self.textField.rightView = UIImageView(image: img)
    //設定圖片填滿右視圖
    self.textField.rightView!.contentMode = UIViewContentMode.ScaleAspectFit
    self.textField.rightView!.clipsToBounds = true
    //顯示下拉符號
    self.textField.rightViewMode = UITextFieldViewMode.Always
    //建立委托
    self.textField.delegate = self
    //設定使用者點擊事件
    self.textField.addTarget(self, action: "selectClicked:", forControlEvents: UIControlEvents.TouchDown);
    。。。。
    }
  11. 最後建立使用者與 UITextField 的互動事件,以及設定 ActionSheetPicker。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class ViewController: UIViewController, UITextFieldDelegate {
    。。。。。
    //設定 textField 唯讀
    func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
    return false
    }
    //不讓使用者透過建盤輸入文字
    func textField(textField: UITextField, shouldChangedCharactersInRange range: NSRange, replacementString string: String) {
    return false
    }
    //使用者點擊事件
    func selectClicked(sender: UITextField) {
    ActionSheetStringPicker.showPickerWithTitle(nil, rows: self.dataArray, initialSelection: 0, doneBlock: {
    picker, value, index in
    self.textField.text = value
    return
    }, cancelBlock: {
    ActionStringCancelBlock in
    return
    }, origin: sender.superview)
    }
    }

Add UITextField on UIView Programmitically by Swift

好像有點久沒有寫文章。。。

最近開始玩 iOS SDK,今天不知道哪根筋不對,突然不想用 Interface Builder 拉 UI,想直接用寫 Code 的方式生,所以我就翻了一下關方手冊,還有拜了 Google 大神!萬事起頭難,我們就先從簡單的開始吧,以下是我找到的方法,請參考!

最簡單的作法

從官方手冊上看到最簡單的辦法就是直接呼叫 UITextField(frame:) 這個建構式,但這個建構式必須傳入一個 CGRect 型別的 frame 變數,到這個 frame 要怎麼建立勒?官方提供了 CGRectMake(x, y, width, height) 這個方法可以快速的產生 UITextField 需要的 frame,說簡單一點就是透過 CGRectMake() 告訴 iOS 這個 UITextField 的外框多大、位置在哪,最後透過 addSubview() 這個方法將 UITextField 放到 view 上面。

1
2
3
4
5
//產生 UITextField 元件
var txtField = UITextField(frame: CGRectMake(10, 200, 300, 30));
//放到 view 上面
self.view.addSubview(txtField);

進階的作法

透過上述的方式可以快速產生一個白白的 UITextField,但如果想產生像 Interface Builder 拉出來的,要怎麼做?除了上面那兩行外,還做一些額外的設定,這樣 UITextField 就可以像 iOS 預設的一樣簡單又漂亮!以下是我的做法:

1
2
3
4
5
6
7
8
9
10
11
var txtField = UITextField(frame: CGRectMake(10, 200, 300, 30));
txtField.borderStyle = UITextBorderStyle.RoundedRect;
txtField.font = UIFont.systemFontOfSize(15);
txtField.placeholder = "Select Drawing";
txtField.autocorrectionType = UITextAutocorrectionType.No;
txtField.keyboardType = UIKeyboardType.Default;
txtField.returnKeyType = UIReturnKeyType.Done;
txtField.clearButtonMode = UITextFieldViewMode.WhileEditing;
txtField.contentVerticalAlignment = UIControlContentVerticalAlignment.Center;
self.view.addSubview(txtField);

Ref. View Programming Guilde for iOS

Use Atlassian Source Tree embedded git in Windows

前些日子因為要在 Mac OS X 上面安裝 Git 的圖形化管理程式,接觸了 Atlassian SourceTree 跟 Github for Mac,但由於工作上有需要使用 Atlassian Bitbucket,所以最後我選擇了 SourceTree。最近因為一些原因工作用的 PC 被還原了,什麼都沒有了,只好一一重裝!裝好 SourceTree 後,我發現 SourceTree 本身就有將 Git for Windows 的執行檔包起來,所以不用另外再裝 Git,但如果想直接使用 SourceTree 內建的 Git 工具要怎麼做呢?以下是我所做的步驟供大家參考:

  1. Atlassian SourceTree 附帶 Git 執行檔的位置在:

    1
    %USERPROFILE%\AppData\Local\Atlassian\SourceTree\git_local\bin
  2. 將 Git 執行檔的位置加到使用者環境變數(User Environment Variables):


    GitImg
  3. 重開命令提示字元(Command Prompt)或是 PowerShell,就可以開心使用摟~

Reference: How to run a git command as a custom action?

Revit debugging on Visual Studio 2013

最近萬惡的微軟(M$)突然大爆發,一直開源又將 Visual Studio 2013 Professional 釋出為 Visual Studio Community 版,免費提供大家使用。用 Visual Studio 2012 也好一陣子了,所以我想換口味試看看~ :P 但裝好 2013 拿來 Debug Revit 2016 增益集(Addin)的時候,卻發現 Revit 掛掉 Crash 了!


RevitImg

所幸拜了 Google 大神後發現了解法,只要將使用除錯器的相容模式(Debugging in Managed Compatibility Mode)就可以解決了!以下是我所做的步㵵供大家參考:
1. 到工具(Tools) > 選項(Options),紅框所選位置:

RevitImg
  1. 點選除錯(Debugging)項目,在點選一般(General),最後將使用相容模式(Use Managed Compatibility Mode)勾選起來,按下確認(OK):


    RevitImg
  2. 可以開心使用 Visual Studio 2013 對 Revit 增益集進行除錯(Trace Debugging)摟~

Reference: Getting Revit to start while debugging API apps with Visual Studio 2013

Mission Control 沒有反應怎麼辦?

最近變的很不愛關電腦,常常螢幕一蓋就收到包包裡了。但久沒有重開機,有些應用程式就會不乖,突然沒有反應了,像這次不乖的就是 Mission Control。它掛掉的當下,我剛好在用 Evernote 語音記事記錄演討會的內容,沒辦法重開機,非常囧!!!還好 Mission Control 可以在不重開機的狀態下重開,以下是我使用的方法,供大家參考!(當然在使用前必需先開起 Terminal ~)

比較暴力的作法

1
$ killall Dock

溫柔的作法

1
$ osascript -e 'quit application "Dock"'

Reference: 10.7: Restart Mission Control

在 Terminal 開啟 Sublime

開使用 Hexo 寫筆記以後,常常都要先 hexo new 一則文章,在用 vim 開啟他。然後問題就來了,我很多時候都要在文章裡面貼上程式碼的內容,但因為我有開啟 vim 的自動縮排功能,直接貼上去後,程式碼就會被縮排縮到找不到內文,自作孽啊… 囧rz

就因為這樣我想找個類似 UltraEdit 的軟體來用,然後聽說 Sublime 似乎不錯,但每次開啟 Hexo 的 Markdown 檔案都要做拖拉的動作,這時候我的懶毛病就犯了!好我想讓 Sublime 跟 vim 一樣可以直接在 Terminal 開啟文章的 md 檔,那要怎麼做呢?以下就是我所做的設定:

  1. 先測試可不可以直接啟動 Sublime

    1
    $ /Applications/Sublime\ Text\ 2.app/Contents/SharedSupport/bin/subl
  2. 如果可以的話,現在為 Sublime 建立一個 alias:

    1
    alias sublime="/Applications/Sublime\ Text\ 2.app/Contents/SharedSupport/bin/subl"
  3. 然後把他加到 shell 的 rc 檔裡面,以下以 zsh 為例:

    1
    2
    $ echo 'alias sublime="/Applications/Sublime\ Text\ 2.app/Contents/SharedSupport/bin/subl"' > ~/.zshrc
    $ source ~/.zshrc

Reference: 在 Terminal 啟動 Sublime Text

git submodule 介紹

簡介

最近為了方便管理專案,將他拆成了好幾個小專案來維護,然後透過參考引入想要的檔案。但 git 沒辦法像 SVN 那樣只單獨下載一個檔案,它必需整個 repository 都 clone 下來,可是整個 clone 下來還要把想要的檔案分別丟到對的位置,這樣好像太麻煩了,還好 git 有提供 submodule 這個機制可以用來做外部資源參考與更新,解救了我這個懶人~~~ XD

新增 submodule

指令:

1
git submodule add {repository} {path/folder name}

範例:

1
git submodule add https://github.com/yiskang/hexo-theme-pure.git theme/pure

注意:在下新增指令前,請注意 {path/folder name} 的部份,先不要新增空目錄,git 自己會新增,不然 會產生衝突,submodule 就會無法新增。不需要為 submodule 建立目錄

新增完後會看到類似下面的內容:

1
2
3
4
5
6
7
Cloning into 'theme/pure'...
remote: Counting objects: 202, done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 202 (delta 14), reused 0 (delta 0), pack-reused 175
Receiving objects: 100% (202/202), 49.48 KiB | 0 bytes/s, done.
Resolving deltas: 100% (84/84), done.
Checking connectivity... done.

這時候輸入 git status ,就會發現多了兩檔案需要 commit:

1
2
3
4
5
6
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitmodules
new file: theme/pure

此外,git 會將 submodule 的資訊記錄在 .gitmodules 裡面:

1
2
3
[submodule "theme/pure"]
path = theme/pure
url = https://github.com/yiskang/hexo-theme-pure.git

最後記得要 commit :

1
git commit -m "Add submodule pure theme"

更新 submodule

每個 submodule 獨自更新:

1
2
cd {module folder}
git pull origin master

但我很懶的,都用這個直接更新整個專案的 submodule

1
git submodule foreach git pull

下載有 submodule 的 repository

下載 remote repository 到 local 端:

1
git clone https://github.com/yiskang/Blog.git

註冊 submodule 到 .gitmodules ,告訴 git 要新增哪些 modules

1
git submodule init

將 submodule 的內容下載下來:

1
git submodule update

移除 submodule

如果不要想使用某個 submodule 了,要移除它要怎麼做呢?我們可以透過下面的步驟將它移除,但在移除前還有一些動作要做!移除 submodule 時得告訴 git 你要移除的 module 名稱,這名稱要去哪找?透過 .gitmodules 就可以啦,在新增 submodule 時,git就將它記錄在 .gitmodules 裡了。那假設現在要移除 pure theme module,我們可以在 .gitmodules 找到這樣的描述:

1
2
3
[submodule "theme/pure"]
path = theme/pure
url = https://github.com/yiskang/hexo-theme-pure.git

module 明稱就是上面的 path,現在要移除 pure theme module,所以就是 theme/pure。 接著就可以開始進行移除了,移除 submodule 的命令就是:

1
2
git submodule deinit {path/folder name}
git rm {path/folder name} or git rm --chched {path/folder name}

現在要將 pure theme module 移除,將上面的 {path/folder name} 換成 theme/pure:

1
2
3
4
5
6
# 先移除 submodule 裡面的內容
git submodule deinit theme/pure
# 移除資料夾
git rm theme/pure
# 或者你不想留下先前的修改記錄,要將它從 Woking Tree 移除
git rm --cached theme/pure

移除完以後,可以從 git status 看到:

1
2
3
4
5
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: .gitmodules
deleted: theme/pure

最後記得 commit:

1
2
git add -A
git commit -m "Remove submodule pure theme"