2014年9月30日 星期二

NSURLConnection programmatically using Swift language

【說明】

此份筆記將紀錄如何下載網路上的東西,並以NSData儲存。


【部分程式碼】

建立連線,指定代理人:

let topPaidAppsFeed: NSString = "http://phobos.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/limit=75/xml"
var appListFeedConnection: NSURLConnection!

var url: NSURL = NSURL(string: topPaidAppsFeed)
var request: NSURLRequest = NSURLRequest(URL: url)

appListFeedConnection = NSURLConnection(request: request, delegate: self)
建立URL的連線,必須有NSURL以及NSURLRequest,並設定自己為NSURLConnection的代理人。

採納Protocol:

NSURLConnectionDataDelegate, NSURLConnectionDelegate
Class必須採納NSURLConnectionDataDelegate與NSURLConnectionDelegate。

實作Method:

func connection(connection: NSURLConnection, didFailWithError error: NSError) {

}
當連線有問題時會觸發的func。

func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
        
}
當接收到回應時會觸發的func,此時我們就可以建立一個NSMutableData的實體。

func connection(connection: NSURLConnection, didReceiveData data: NSData) {
        
}
資料會被分成好幾個區塊傳送,此時就會透過didReceiveData來接收每一個區塊,所以我們就可以利用NSMutableData裡的appendData將區塊的資料合併成一個完整的資料。

func connectionDidFinishLoading(connection: NSURLConnection) {


}
當資料傳送完成時會觸發的func,此時就可以得到下載完成的NSMutableData,使用NSXMLParser就可以解析Data,讓我們應用。

2014年9月13日 星期六

MKAnnotationView programmatically using Swift language

【說明】

此份筆記將紀錄如何將原本在地圖上面的針換成自訂的圖片。


【片段程式碼】

該Class需成為MKMapView的代理人,並實作viewForAnnotation這個method,如下所示。

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView {
        
    var annotationView: MKAnnotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "anno")
    annotationView.image = UIImage(named: "pin.png")
    annotationView.canShowCallout = true
        
    return annotationView

}
canShowCallout是決定Annotation點選時是否跳出View。


【執行結果】

2014年9月8日 星期一

Self Sizing Cells programmatically using Swift language

【說明】

此份筆記出處來自於這篇

此份筆記將紀錄當資料超出UILabel時要如何自動換行顯示,確保所有資料都能正確的顯示出來。

原始資料,如下圖所示。

原始資料


【片段程式碼】

首先先替UILabel加入Auto-Layout,如下圖所示。

nameLabel加入Auto-Layout

addressLabel加入Auto-Layout

設定Label的Line為0,如下圖所示。(此處僅示範nameLabel)

設定Label的Line數

在viewDidLoad內加入兩行程式碼,如下所示。

tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension
告訴TableView預計的Row高度為68。將TableView row的高度設定為UITableViewAutomaticDimension。


【執行結果】




【專案範例】

2014年9月1日 星期一

NSUserDefaults programmatically using Swift and Object-C language

【說明】

此份筆記將紀錄如何使用NSUserDefaults來紀錄一些使用者的預設值。

可以儲存Bool、Float、Integer、Object、Double、Double,適合儲存程式一執行的使用者預設值。

NSUserDefaults是用plist在做儲存。


【片段程式碼】

儲存值的方式:

<Swift>

NSUserDefaults.standardUserDefaults().setValue("Hello", forKey: "stringKey")

<Object-C>

[[NSUserDefaults standardUserDefaults] setObject:@"Hello" forKey:@"stringKey"];
若只有執行這行程式碼並不會真正的將值儲存,因為Hello這個字串目前只是儲存在暫存記憶體內而已,還沒有寫到可以永久保存的區塊,必須執行synchronize才會將值真正的儲存起來,如下所示。

<Swift>

NSUserDefaults.standardUserDefaults().synchronize()

<Object-C>

[[NSUserDefaults standardUserDefaults] synchronize];

取得值的方式:

<Swift>

NSUserDefaults.standardUserDefaults().stringForKey("stringKey")

<Object-C>

[[NSUserDefaults standardUserDefaults] stringForKey:@"stringKey"];

刪除值的方式:

<Swift>

NSUserDefaults.standardUserDefaults().removeObjectForKey("stringKey")

<Object-C>

[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"stringKey"];

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,對應到的監聽者就會收到所發送的狀態。


【執行結果】

    

    

    


【專案範例】