About

| Posted by Sam Lee

Swift - Using CocoaPod

Cocoapod 是一套3rd party plugins/framework 版本管理工具 for IOS .
透過設定擋的方式 統一管理你要使用的 plugins 以及版本
然後 install 就可以全部幫您解決 upgrade/downgrade 的問題 !

  1. 安裝
    • 先要安裝好 Ruby 在您的系統裡 (這部分請 Google "install ruby")
    • 打開 terminal , 'sudo gem install cocoapods' 安裝 cocoapods
    • 安裝完後先執行 pod setup 讓cocoapods setup master repo P.S. 如果遇到 pod package找不到的問題可以 pod repo remove master , 再 pod setup 重新設定 master repo
  2. 設定
    • 新開一個 xcode project
    • 打開 terminal , 'cd /go/to/xcode/Project/'
    • vi Podfile 新增編輯 Podfile (這邊以安裝 pop 為例)
    • 完成後 離開 vi , 輸入 'pod install' 完成!
    • open abc.xcworkspace 由 pod 更改過的新的 workspace
    • 接下來就可以開始使用 pod 安裝的 3rd plugins/frameworks 來寫code 勒~!
| Posted by Sam Lee

Swift - ActivateIndicator , RefreshControl

最近開始在 App 上弄上 Loading 的元件
目前關於 Loading 相關的(原生的) 就是 UIActivityIndicatorView , UIRefreshControlProgress View
目前用了前面兩種, 使用原生系列的還蠻簡單使用的, 但是如果要客製化的話相對得花上不少功夫 ...
在此先介紹簡單的用法, 之後研究到在補上客製化的用法~

  1. UIActivityIndicatorView
    這就是在畫面上跑出一顆小小的轉圈圈 Loading 使用方法就是
    先宣告 UIActivityIndicatorView 在加入到你想要放進的 View 就可以
    之後要出現就 startAnimating() , 停止就是 stopAnimating()

    func initActivityIndicator(){
    self.activityIndicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 100, 100))
    self.activityIndicator.center = self.view.center
    self.activityIndicator.hidesWhenStopped = true
    self.activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
    self.view.addSubview(self.activityIndicator)
    
    //show loading icon
    self.activityIndicator.startAnimating()
    }
    
  2. UIRefreshControl
    這是在 iOS6 後才官方才加入的新 API, 在這之前有很多第三方的Plugin 可以使用, 而目前官方的只能 support TableView, 所以如果要用在其他 View 的可能要找其他第三方的 Plugin 才行

        self.refreshControl = UIRefreshControl();
    self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh");
    //set refresh Event
    self.refreshControl.addTarget(self, action: "refreshHotPlace", forControlEvents: UIControlEvents.ValueChanged);
    //加入到 tableView Controller
    self.tableView.addSubview(self.refreshControl);
    

    就這樣就可以使用了 , 關掉 Loading 的方法 self.refreshControl.endRefreshing();

以上~~

| Posted by Sam Lee

Swift - Google Map SDK

在開發 LBS (Location-Based Service) 地圖一定是必備的, 在 iOS 上有兩個比較常用的選擇, 一個是內建的 Apple Map 另一個就是在下覺得最好用的 Google Map, 以下就介紹如何使用 Google Map SDK

參考網址: https://developers.google.com/maps/documentation/ios/start?hl=zh-tw

照參考網址的步驟

  1. 下載 SDK
  2. 將 GoogleMaps.framework 拖拉近你的 Project
  3. 在 Project 中選擇 GoogleMaps.framework 按下滑鼠右鍵 選 show in Finder
  4. 將 Resources/GoogleMaps.bundle 拉出來到 Project裡
  5. 到 Build Phases 中的 Link Binary with Libraries 新增需要的 Framework
  6. 新增以下Frameworks

    AVFoundation.framework
    CoreData.framework
    CoreLocation.framework
    CoreText.framework
    GLKit.framework
    ImageIO.framework
    libc++.dylib
    libicucore.dylib
    libz.dylib
    OpenGLES.framework
    QuartzCore.framework
    SystemConfiguration.framework
    
  7. 到 Build Settings 裡的 Other Linker Flags 新加上 -ObjC

  8. 是 Swift 所以我們到 Bridge-Header 新增 (不知道什麼是 Bridge-Header 可參考 Swift-Facebook

    #import <GoogleMaps/GoogleMaps.h>
    
  9. 到 AppDelegate.swift 的 didFinishLaunchingWithOptions 加上

    GMSServices.provideAPIKey("APIKEY_FROM_GOOGLE"); //input your API KEY in it.
    
  10. 準備完成 先到ViewController.swift 新增

    @IBOutlet
    var MapView : GMSMapView!
    
  11. 到 StoryBoard 放一個 View 把 View 的 Class 指定成 GMSMapView 在和ViewController裡的 MapView 做連結

  12. 初始化 Map

        var camera : GMSCameraPosition = GMSCameraPosition.cameraWithLatitude(25.02, longitude: 25.055, zoom: 15);
    self.MapView.camera = camera;
    self.MapView.myLocationEnabled = true;
    
  13. 執行 App 就可以看到 Google Map 了

  14. One more thing.... 新增 Marker

        //creat maker
    var position = CLLocationCoordinate2DMake(25.02, 25.055);
    var placeMaker : GMSMarker = GMSMarker(position: position)
    
    placeMaker.title = "Hello Map; //Title
    placeMaker.snippet = "Hello World"; //Desc
    placeMaker.map = self.MapView;
    
  15. 這樣就會出現 Map + Marker 了 !

結語:
雖然 Google Map 不是 iOS 原生的, 但他本來的操作是大家所熟悉的所以我還是比較喜歡使用, 還可以融合街景, 計算路徑等等的功能, 應用範圍還是蠻廣的!

| Posted by Sam Lee

Swift - Using SDWebImage

弄了一個有點多的 image table view 結果超慢, 後來想起有個神兵利器叫做 SDWebImage , 用了之後整個超順阿!!!
以下來介紹一下怎麼使用 SDWebImage in Swift ! (其實跟 Objective-C沒差太多, 只是要透過 Bridge 去 Import)

SDWebImage URL: https://github.com/rs/SDWebImage

  1. 先Download 整個Project
  2. File -> Add File to XXX -> 選擇 SDWebImage.xcodeproj

  3. 設定 Project 的 Build Phases -> Target Dependencies 按 + 選擇 SDWebImage 要使用的 Lib

  4. 在 Link Binary With Libraries 裡加入 ImageIO.framework , libSDWebImage.a(或是其他兩個取決於使用哪個Lib)

  5. 在 Build Settings 裡找到 Linking -> Other Linker Flags 加上字串 -ObjC

  6. 在 Swift 和 Object-C 的 Bridge file 裡加入

    #import <SDWebImage/UIImageView+WebCache.h>
    

Done! 這樣就能開心使用 SDWebImage 了~
附上 Table View 的 Cell 範例..一行搞定

cell?.UI_Image.sd_setImageWithURL(NSURL(string: self.hotPlaceDataArr[indexPath.row].pic as String))

P.S. Swift 和 Objective-C 的 Bridge file 可以參考
Swift - Integrate Facebook SDK

| Posted by Sam Lee

Swift - Facebook Multi Request

Facebook 最近更新了 Graph API 2.1 , 導致原本 2.0 的 FQL API 已經不能使用, 現在如果要查詢多筆資料的話, 就必須得使用新的 Batch Request (Multi Request)...

參考 URL : https://developers.facebook.com/docs/graph-api/making-multiple-requests/

ok 假設我現在有 30 個 id 要查, 所以先宣告一個 array 存放 30筆id資料 然後開始!

  1. 先宣告一個 FBRequestConnection (這是要拿來裝 Request 的)
  2. 在迴圈中 宣告 FBRequest 並填入對應的參數
  3. 將 FBRequest 放進 FBRequestConnection (利用 addRequest) 並加上 CompletionHandler
  4. FBRequestConneciton.start() 開始發送 Request

範例:

var fbConnection : FBRequestConnection = FBRequestConnection();

for (idx, placeID) in enumerate(idArr) {
    var request1 : FBRequest = FBRequest(graphPath:"/\(placeID)", parameters: nil, HTTPMethod: "GET");
  fbConnection.addRequest(request1, completionHandler: {(connection:FBRequestConnection!, result:AnyObject!, error:NSError!) in
                            if(!Error){
                    if let resultObj = result as? FBGraphObject{
                        println(resultObj);
                    }
                }else{
                    // Get Error ...
                    println(Error);
                }
            }, batchEntryName: nil)
fbConnection.start();

這邊要注意的是 FB 在 Request數量上有做限制, 最大只能到 40 筆!
所以需要查詢更多的可能要在這次查詢結束後繼續查詢 ...這是目前覺得很不方變得地方阿!!!

以上~ 有好方法的話請通知我阿~~~ 感謝!!

| Posted by Sam Lee

Swift - Segue 傳值

介紹一下如何用 Segue 傳值, 當然也是 Swift 版本的

  1. 首先在 Storyboard 新增一個 ViewController

    First add new viewController into your storyboard.

  2. 選擇 Root View Controllerview controller icon 按住ctrl 然後把線拖放到 新的ViewController

    Choose view controller icon on the top of root view controller, then press ctrl drag the line to new view controller to add new segue.

  3. 先選 Show 就好

  4. 新增一個 Swift file : ReceiveSegueDataViewController(名稱可自取)

    Add new swift file : ReceiveSegueDataViewController(Name can change by yourself)

    import Foundation
    class HotPlaceMainViewController : UIViewController {    
    var receiveData: String!
    override func viewDidLoad() {
        super.viewDidLoad();
        println(receiveData);
    }    
    }
    
  5. 在Storyboard中, 設定新的View Controller 的 class 為 HotPlaceMainViewController

    Set HotPlaceMainViewController as new view controller for New controller View in StoryBoard.

  6. 設定 Segue 的 identifier 為 sendData 如下圖, 用來辨識是哪一個 Segue 要傳什麼值過去

    Set the identifier "sendData" to the segue. You can refer the below image , that is used to identify which segue we used to pass value.

  7. 回到 Root View Controller 新增 Override 的 function : prepareForSegue

    Let's add new override function "prepareForSegue" to handle view change by segue.

    override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    if segue.identifier == "sendData" {
          //get destination controller
        var destViewController = segue.destinationViewController as HotPlaceMainViewController;
        destViewController.receiveData = "SegueData!!!!!";
    }
    }
    
  8. 完成. 這樣在 View 準備換場時就會呼叫 prepareForSegue 在裡面可以得到 destination View controller 的 Object 然後把值放進去 達成傳值的動作

    Complete! So before root view change to next view that will call prepareForSegue to help you set the data to destination view object's variable to comeplete the action.

P.S. 傳值不可以指定下一個View 裡的 UI Object, 不然會有找不到的問題
You can't set value to UI object in destination view or you will get nil pointer error in runtime.

| Posted by Sam Lee

Swift - Integrate Facebook SDK

  1. 先 Download Facebook SDK ~ Facebook developer page
  2. 把 FacebookSDK.framework 加入 project 中
  3. 重點來了, 因為 SDK 是 Objective-C 所以得使用 Bridge 的方式才能 import

    • 新增一個 Objective-C header file 內容只要這個就可以了
        #import <FacebookSDK/FacebookSDK.h>
      
    • 到 Build Settings 裡面找到 Swift Compiler - Code Generation ,設定 Objective-C Bridging Headeryour_projectname/Bridge-Header.h 記得不要設定到 for Project 的那格 可參考圖片
    • 確定 Install Objective-C Compatibility Header 為 Yes
    • Done
  4. 在 AppDelegate.swift 中 加入 ...

    func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
        // Override point for customization after application launch.
        FBLoginView.self
        FBProfilePictureView.self
        return true
    }
    
    func application(application: UIApplication, openURL url: NSURL, sourceApplication: NSString?, annotation: AnyObject) -&gt; Bool {
    var wasHandled:Bool = FBAppCall.handleOpenURL(url, sourceApplication: sourceApplication)
    return wasHandled
    }
    
  5. 在 ViewController.swift 加上一些東西

    class ViewController: UIViewController, FBLoginViewDelegate {
        @IBOutlet
        var fbLoginView : FBLoginView!
        override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    
        self.fbLoginView.delegate = self
        //grant permission from facebook
        //self.fbLoginView.readPermissions = []
    }
    
    //MARK: Facebook Delegate Methods
    func loginViewShowingLoggedInUser(loginView: FBLoginView!) {
        println("User Logged In");
    }
    
    func loginViewFetchedUserInfo(loginView : FBLoginView!, user: FBGraphUser) {
        println("User: \(user)")
        println("User ID: \(user.objectID)")
        println("User Name: \(user.name)")
        var userEmail = user.objectForKey("email") as String
        println("User Email: \(userEmail)")
    }
    
    func loginViewShowingLoggedOutUser(loginView : FBLoginView!) {
        println("User Logged Out")
    }
    
    func loginView(loginView : FBLoginView!, handleError:NSError) {
        println("Error: \(handleError.localizedDescription)")
    }
    
  6. 在 StoryBoard 中加入 UIView 並把 Class 設成 FBLoginView

  7. 將 UIView(FBLoginView) 綁定到 ViewController中的 fbLoginView

  8. Follow Facebook 的步驟的話我們得去 plist 裡加點字段

  9. Done ~ 執行 就會看到!!

| Posted by Sam Lee

Swift - How to get location

基本上跟 Objective-C 的用法差不多 , 只是換成 Swift 語法

  1. 增加兩個字串到 info.plist (type : String)
    • NSLocationWhenInUseUsageDescription
    • NSLocationAlwaysUsageDescription
  2. import CoreLocation
  3. 繼承 CLLocationManagerDelegateViewController Class
  4. 設定 CLLocationManagerDelegate 的 function 來接收資料
  5. 別忘記 locationManager.startUpdatingLocation() << 這樣才會開始抓 location 資料
  6. 為了不一直抓取位置資訊 在抓到資訊的 delegate func 中加上 locationManager.stopUpdatingLocation() 來停止抓取位置資訊

Sample Code.

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

    var locationManager : CLLocationManager!
    var currentLocation : CLLocation?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.initLocationManager()
    }

    func initLocationManager(){
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()
    }

    //MARK: Location Manager Delegate
    
    // get error
    func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!){
        println(error)
    }
    
    //get location
    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        
        locationManager.stopUpdatingLocation()
        var locationArray = locations as Array
        self.currentLocation = locationArray[locationArray.count-1] as? CLLocation

        println(self.currentLocation)

        //getHotPlaceFromFacebook(self.currentLocation)
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

以上~~!!

| Posted by Sam Lee

Phonegap / Cordova + Yeoman + Webapp

前言

最近回鍋看了一下 Phongap 居然已經出到 3.0 ! 曾經以為這東西應該慢慢會被 Native App 取代掉的 , 到現在又更新了而且Support 的平台和完整性也變很高 !! 想當然就想要和最近在公司學到的東西作結合.
結果查了一下真的有人已經有在用這個Solution了 , 所以就簡單筆記一下吧 .

Phonegap + Yeoman + Webapp

Install Phonegap

sudo install -g phonegap

Install Yeoman

sudo install -g yo

建立一個 Phonegap 的 project

phonegap project_path "com.project_name.app" "app_name"

create for Android or iOS

cd project_path
phonegap build android

init yeoman

yo webapp

copy Phonegap Config file

copy ../www/config.xml ./app/

Change GruntFile.js

這邊的設定主要是, Grunt build 時把我們Yeoman 產生的web file 全數取代 phonegap 當中的 www
所以在執行 grunt build 時 會先 clean(清除 phonegap 中的 www) , 在 Copy (連xml一起copy)
最後 Deploy 到 ../www

var yeomanConfig = {
  app: 'app',
  dist: '../www'
};

clean: {
  options: {
    force: true
  },
  ...

copy: {
  dist: {
    files: [{
      expand: true,
      dot: true,
      cwd: '<%= yeoman.app %>',
      dest: '<%= yeoman.dist %>',
      src: [
        '*.{ico,png,txt,xml}',
        '.htaccess',
        'images/{,*/}*.{webp,gif}',
        'styles/fonts/*'
      ]
    }
   ...

Test it!!

先用 Grunt 把 HTML 的File build 到 ../www 然後 Compile 進Android 或是 Android Emulator.

grunt build
phonegap run android

結論

Phonegap 3.0 真是還蠻不錯的, 可以直接Compiler 進 Android , 完全不用打開肥大的 IDE 好爽~ , 加上Yeoman 把整個FED 環境弄起來, 5min 內就能佈署好 Mobile App 的開發了 ya~~

題外話

本來是用 angular 想說來練習一下的, 不知道是哪邊的問題在 deploy 到手機後, 反而畫面是全白的 ... 目前原因不明, 所以先使用最基本的webapp generator 來產生, 就一切正常了 , 真是太詭異了!! Anyway 接下可以開始準備Coding囉!!