2014年8月29日 星期五

CLLocationManager programmatically using Swift and Object-C language

【說明】

此份筆記將紀錄如何取得目前裝置的經緯度。

此份專案有兩個頁面,其功能皆相同,但是第一個頁面所取得的經緯度為當下所偵測到的經緯度,第二個頁面取得的經緯度會一直更新,也就是說可以偵測移動的經緯度。


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為location的專案,使用iPhone裝置。

設計使用者介面:

使用TabBarController包著兩個NavigationController,每個NavigationController各帶有一個View,編號1的TapBarItem名稱為First,File's owner設定為ViewController,編號2的TapBarItem名稱為Second,File's owner設定為SecondViewController,並讓Storyboard內會用到的元件與相對應的Class做連結,如下圖所示。

使用者介面

加入Location的Frameworks:

<Swift>
加入Frameworks至專案內,在Class內import即可,如下所示。

加入CoreLocation的Frameworks

在Class內import即可。

import CoreLocation

<Object-C>
加入Frameworks至專案內,在Class內import即可,如下所示。

#import <CoreLocation/CoreLocation.h>

修改ViewController:

建立CLLocationManager實體,如下所示。

<Swift>

var locationManager: CLLocationManager = CLLocationManager()

<Object-C>

宣告資料型態為CLLocationManager,名為locationManager的變數,並宣告可以Getter/Setter,如下所示。

@property (strongnonatomicCLLocationManager *locationManager;

在viewDidLoad時建立實體,並使用預設的初始值,如下所示。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.locationManager = [[CLLocationManager allocinit];
}

當按鈕按下後要取得目前的經緯度,並顯示在TextField內,如下所示。

<Swift>

@IBAction func getPosition(sender: UIButton) {
    locationManager.startUpdatingLocation()
    var latitude: CLLocationDegrees = locationManager.location.coordinate.latitude
    var longitude: CLLocationDegrees = locationManager.location.coordinate.longitude
    latitudeField.text = NSString(format: "%f", latitude)
    longitudeField.text = NSString(format: "%f", longitude)
}

<Object-C>

- (IBAction)getPosition:(UIButton *)sender {
    [self.locationManager startUpdatingLocation];
    CLLocationDegrees latitude = self.locationManager.location.coordinate.latitude;
    CLLocationDegrees longitude = self.locationManager.location.coordinate.longitude;
    self.latitudeField.text = [NSString stringWithFormat:@"%.f", latitude];
    self.longitudeField.text = [NSString stringWithFormat:@"%.f", longitude];
}
locationManager開始更新目前的位置。取得緯度。取得經度。顯示緯度在TextField內。顯示經度在TextField內。

修改SecondViewController:

要能偵測持續偵測經緯度必須使用CLLocationManagerDelegate,而要使用代理人機制就必須要有三步驟:

1.  告訴代理人是誰,在viewDidLoad內新增程式碼,如下所示。

<Swift>

override func viewDidLoad() {
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    locationManager.distanceFilter = 3.0
}

<Object-C>

- (void)viewDidLoad {
    [super viewDidLoad];
    self.locationManager.delegate = self;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    self.locationManager.distanceFilter = 3.0;
}
告訴CLLocationManagerDelegate代理人是自己。設定所需要的精準度。設定距離多久偵測一次,單位為meters。

2.  代理人需採納Protocol,如下所示。

<Swift>

class SecondViewController: UIViewController, CLLocationManagerDelegate {
}

<Object-C>
@interface SecondViewController ()<CLLocationManagerDelegate>
@end

3.  代理人實作Method,如下所示。

<Swift>

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
    var locationArray: NSArray = locations
    var location: CLLocation = locationArray.lastObject as CLLocation
    var latitude: CLLocationDegreeslocation.coordinate.latitude
    var longitude: CLLocationDegreeslocation.coordinate.longitude
    latitudeField.text = NSString(format: "%f", latitude)
    longitudeField.text = NSString(format: "%f", longitude)
}

<Object-C>

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    CLLocation *location = [locations lastObject];
    CLLocationDegrees latitude = location.coordinate.latitude;
    CLLocationDegrees lontitude = location.coordinate.longitude;
    self.latitudeField.text = [NSString stringWithFormat:@"%f", latitude];
    self.longitudeField.text = [NSString stringWithFormat:@"%f", lontitude];
}

當按鈕被按下後就開始偵測,如下所示。

<Swift>

@IBAction func getPosition(sender: UIButton) {
    locationManager.startUpdatingLocation()
}

<Object-C>

- (IBAction)getPosition:(UIButton *)sender {
    [self.locationManager startUpdatingLocation];
}

當Switch打開時開始偵測,關閉時停止偵測,如下所示。

<Swift>

@IBAction func switchChange(sender: UISwitch) {
    if sender.on {
        self.locationManager.startUpdatingLocation()
    } else {
        self.locationManager.stopUpdatingLocation()
        latitudeField.text = nil
        longitudeField.text = nil
    }
}

<Object-C>

- (IBAction)switchChange:(UISwitch *)sender {
    if (sender.isOn) {
        [self.locationManager startUpdatingLocation];
    } else {
        [self.locationManager stopUpdatingLocation];
        self.latitudeField.text = nil;
        self.longitudeField.text = nil;
    }
}
當停止偵測時也將latitudeField與LongitudeField內的文字清除。


【執行結果】

    



【專案範例】

XMLParser programmatically using Swift and Object-C language

【說明】

此份筆記將紀錄如何解析XML檔案。

情境:
當從網路接收到XML檔案後將其儲存為NSData,如下所示。

<?xml version="1.0" encoding="utf-8"?>
<double xmlns="http://www.webserviceX.NET/">3.4822</double>

我們要利用XMLParser解析,取得double內的值(3.4822)。


【片段程式碼】

假設接收到的資料儲存在名為receiveData,如下所示。

<Swift>
var receiveData:NSData = NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: &urlResponse, error: &error)!

<Object-C>
NSData *receiveData=[NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&urlResponse error:&error];

我們建立一個funciton專門來處理解析XML的動作,如下所示。

<Swift>
func parserXML(xml: NSData) {

}

<Object-C>
- (void)parserXML:(NSData*)xml {

}
function名為parserXML,傳入NSData。

撰寫處理XML的程式,如下所示。

<Swift>
var xmlParser: NSXMLParser = NSXMLParser(data: xml)
xmlParser.delegate = self

xmlParser.parse()

<Object-C>
self.xmlParser = [[NSXMLParser alloc] initWithData:xml];
self.xmlParser.delegate = self;

[self.xmlParser parse];
建立一個NSXMLParser,並初始化傳入NSData。使用代理人機制的第一步,指定代理人是誰。執行method開始解析。

使用代理人機制的第二步,代理人必須採納Protocol,如下所示。

<Swift>
class ViewController: UIViewController, NSXMLParserDelegate {
}

<Object-C>
@interface ViewController ()<NSXMLParserDelegate>
@end

使用代理人機制的最後一步,實作Method,如下所示。

<Swift>
func parser(parser: NSXMLParser, foundCharacters string: String!) { 
    println("\(string)")
}

<Object-C>
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    NSLog(@"%@", string);
}
利用foundCharacters函式可以解析出double標籤內的值,第一行的標籤並不會被解析。

透過簡單的幾行程式即可解析出想要的資料,這只是較簡單的XML解析,未來還有更複雜的解析方式。

2014年8月22日 星期五

Compress Image programmatically using Swift and Object-C language

【說明】

此份筆記將紀錄如何使用畫布來改變Image的大小。


【片段程式碼】

<Swift>
UIGraphicsBeginImageContextWithOptions(CGSizeMake(48.0, 48.0), false, UIScreen.mainScreen().scale)
image.drawInRect(CGRectMake(0, 0, 48, 48))
var smallImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

<Object-C>
UIGraphicsBeginImageContextWithOptions(CGSizeMake(48.0, 48.0), NO, [UIScreen mainScreen].scale);[image drawInRect:CGRectMake(0, 0, 48.0, 48.0)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
建立48*48的畫布,不透明度設否,比例使用偵測的方式調整。將圖片畫至畫布內。取得畫布的圖片。釋放畫布。


【執行結果】

2014年8月21日 星期四

Detect Internet status programmatically using Swift and Object-C language

【說明】

此份筆記將紀錄如何偵測網路的連接型態以及監聽網路是否有斷線。


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為connectingAbility的專案,使用iPhone裝置。

設計使用者介面:

使用TabBarController包著兩個NavigationController,每個NavigationController各帶有一個View,在兩個View上面各加一個Button,並替每個ViewController設定File's owner,如下圖所示。
設計使用者介面

加入Reachability:

Reachability這個Class是由Apple提供的,定義一些method讓我們可以抓取到裝置網路的狀況,我們可以從Apple提供的Sample Code專案內取得這個Class,如下圖所示。
Reachability專案

設定Reachability Class:

修改Reachability.m檔,在dealloc的函式內加入[super dealloc],如下圖所示。
修改dealloc函式

告訴Compiler關閉Reachability.m的ARC,如下圖所示。
關閉ARC

加入所需要的Framework至專案內,如下圖所示。
加入Framework

import Reachability Class:

Swift:
需要建立一個與Object-C橋接的檔案,請參考這裡,在橋接檔案內import Reachability.h檔,之後的每個ViewController皆可使用Reachability這個Class,不需額外import,如下圖所示。
修改橋接檔案內容

Object-C:
在所需要用到Reachability Class的.m檔內新增#import "Reachability.h",如下圖所示。
import h檔

修改StatusViewController:

當按鈕按下後開始判斷所適用的網路是WiFi、WWAN或是沒連線,將以下程式碼加入到接收按鈕按下的func內,如下所示。

<Swift>
@IBAction func click(sender: UIButton) {
    let statusReach: Reachability = Reachability.reachabilityForInternetConnection()
    let networksStatus: NetworkStatus = statusReach.currentReachabilityStatus()
    var status: NSString!
    if networksStatus.value == 0 {
        status = "NoReachable"
    } else if networksStatus.value == 1 {
        status = "ReachableViaWiFi"
    } else {
        status = "ReachableViaWWAN"
    }
    navigationItem.prompt = status
    var timer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: "cancelPrompt", userInfo: nil, repeats: false)
}

func cancelPrompt() {
    navigationItem.prompt = nil
}

<Object-C>
- (IBAction)statusClick:(UIButton *)sender {
    Reachability *statusReach = [Reachability reachabilityForInternetConnection];
    NetworkStatus networkStatus = [statusReach currentReachabilityStatus];
    NSString *status;
    switch (networkStatus) {
        case NotReachable:
            status = @"NotReachable";
            break;
        
        case ReachableViaWWAN:
            status = @"ReachableViaWWAN";
            break;
            
        case ReachableViaWiFi:
            status = @"ReachableViaWiFi";
            break;
            
        default:
            break;
    }
    self.navigationItem.prompt = status;
    [self performSelector:@selector(cancelPrompt) withObject:nil afterDelay:10];
}

- (void)cancelPrompt {
    self.navigationItem.prompt = nil;
}
statusReach判斷現在是否連接上網路。networkStatus用來儲存目前連接網路的類型。得到連接網路的類型後去將status這個NSString設定文字。將status文字顯示在Navigation的Prompt內。延遲10後在去執行cancelPrompt這個method。

修改ChangeViewController:

將以下程式碼加入到viewDidLoad內,如下所示。

<Swift>
NSNotificationCenter.defaultCenter().addObserverForName(kReachabilityChangedNotification, object: nil, queue: NSOperationQueue.mainQueue()) { (NSNotification) -> Void in
    let networksStatus: NetworkStatus = self.internetReachability.currentReachabilityStatus()
    var status: NSString!
    if networksStatus.value == 0 {
        status = "Disconnection"
    } else if networksStatus.value == 1 {
        status = "Connection"
    } else {
        status = "Connection"
    }
    self.navigationItem.prompt = status
}

<Object-C>
[[NSNotificationCenter defaultCenteraddObserverForName:kReachabilityChangedNotification object:nil queue:[NSOperationQueue mainQueueusingBlock:^(NSNotification *note) {
    NetworkStatus networkStatus = [self.internetReachability currentReachabilityStatus];
    NSString *status;
    switch (networkStatus) {
        case NotReachable:
            status = @"Disconnection";
            break;
            
        case ReachableViaWWAN:
            status = @"Connection";
            break;
                
        case ReachableViaWiFi:
            status = @"Connection";
            break;
                
        default:
            break;
    }
    self.navigationItem.prompt = status;
}];
像NSNotificationCenter註冊一個監聽者。networkStatus取得目前網路的狀態。判斷是否可以連線。將狀態給NavigationItem的Prompt去顯示。

當按鈕按下後開始監聽網路連線狀態是否有改變,如下所示。

<Swift>
@IBAction func click(sender: UIButton) {
    self.internetReachability = Reachability.reachabilityForInternetConnection()
    self.internetReachability.startNotifier()
}

<Object-C>
- (IBAction)checkStatus:(UIButton *)sender {
    self.internetReachability = [Reachability reachabilityForInternetConnection];
    [self.internetReachability startNotifier];
}
當按鈕按下後internetReachability取得是否有連線。執行startNotifier,當連線的狀況有改變時會發送Notification給NSNotificationCenter,對應到的監聽者就會收到所發送的狀態。


【執行結果】

    

    

    


【專案範例】

2014年8月20日 星期三

Pass Value by NSNotificationCenter programmatically using Swift and Object-C language

【說明】

先前已經會了使用自訂DelegateBlock的方式來將值回傳,這次我們將要使用NSNotificationCenter來達到這樣的目的。

iOS有五種傳值的方式:
1.  建立對方Class的實體變數,取得物件後做getter/setter。
2.  利用target-action的機制,傳值給func。
3.  自訂Delegate。
4.  Block。
5.  NSNotificationCenter。

比較自訂Delegate、Block與NSNotificationCenter:
自訂Delegate:需要六步驟,需倚靠prepareForSegue取得Class的指標。
Block:需要三步驟,需倚靠prepareForSegue取得Class的指標。
NSNotificationCenter:需要兩步驟,不需倚靠prepareForSegue取得Class的指標,若不需監聽通知需要移除註冊的通知。

使用NSNotificationCenter回傳值的兩步驟:
1.  向NSNotificationCenter註冊一個Notification。
2.  當事件發生時,發送Notification給NSNotificationCenter。


【專案開發步驟】

建立專案:

使用Single View Application模板建立一個名為Notification的專案,使用iPhone裝置。

設計使用者介面:

替View加入NavigationController,並完成介面的設計,並將會用到的元件與對應的ViewController做連結,如下圖所示。

設計使用者介面

建立並修改B ViewController:

新增Class,並取名為DetailViewController,並與Storyboard內的ViewController做連結,如下圖所示。

<Swift>
宣告Storyboard內的ViewController的Class是誰_Swift
<Object-C>
宣告Storyboard內的ViewController的Class是誰_Object-C


當事件發生時,發送Notification給NSNotificationCenter,如下所示。

<Swift>
@IBAction func click(sender: UIButton) {
    NSNotificationCenter.defaultCenter().postNotificationName("SendName", object: nameTextField.text)
    self.navigationController.popToRootViewControllerAnimated(1);
}

<Object-C>
- (IBAction)click:(UIButton *)sender {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"SendName" object:self.nameTextField.text];
    [self.navigationController popToRootViewControllerAnimated:YES];
}
當按鈕按下後執行func名為click的func。發送Notification給NSNotification,並帶物件nameTextField的文字。用NavigationController回到RootViewController。

修改A ViewController:

將Class名改為MainViewController,並與Storyboard內的ViewController做連結,如下圖所示。

<Swift>
宣告Storyboard內的ViewController的Class是誰_Swift
<Object-C>
宣告Storyboard內的ViewController的Class是誰_Object-C

向NSNotificationCenter註冊一個監聽者,於ViewDidLoad內加入程式碼,如下所示。

<Swift>
NSNotificationCenter.defaultCenter().addObserverForName("SendName", object: nil, queue: NSOperationQueue.mainQueue()) { (NSNotification) -> Void in
    self.nameLabel.text = NSNotification.object as String
}

<Object-C>
[[NSNotificationCenter defaultCenter] addObserverForName:@"SendName" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
    self.nameLabel.text = note.object;
}];
向NSNotificationCenter註冊一個監聽者,監聽名為SendName的Notification所發送的通知,使用mainQueue去執行工作,將收到的通知給nameLabel去顯示。


【執行結果】

    

    


【專案範例】