prettyprint
2017年12月22日 星期五
開發iPhone App, 選擇 Swift 或 Objective-C?
Swift 語言優於 Objective-C,這爭議由Simon Ng 知名作家的"iOS 10 App 程式設計實力超進化實戰攻略“一書的範例可看出:
Objective-C
const int count = 10;
double price = 23.55;
NSString *firstMessage = @"Swift is awesome.";
NSString *secondMessage = @"What to you think?";
NSString *message = [NSString stringWithFormat:@"%@%@",firstMessage, secondMessage];
NSLog(@"%@",message);
Swift
let count = 10
var price = 23.55
let firstMessage = "Swift is awesome."
let secondMessage = "What to you think?"
let message = firstMessage + secondMessage
print(message)
哇!Swift 在語法Syntax上的表達簡潔而有力!
首先,每行程式 (Statement) 的結尾: Swift 不用分號(; semicolon) 來作為每行程式的結尾。這是 C/C++/C#/Java 慣用的寫法。
第二:常數與變數的宣告。在Swift的常數以 let 宣告,而變數則以 var 宣告。這點與 Objective-C 在常數宣告必須特別指定用 const 並無法說俗優俗劣。但,Swift 的常數或變數不需要特別定義其資料型態 (Data Type),而是Swift 可以根據其指定的值來推論 (Infer) count 是整數型態,而 price 是 double 資料型態。
當然 ,Swift 宣告整數或變數時,也可定義其 Data Type。例如:
let count : Int = 10
var price : Double = 23.55
第三:字串的宣告簡潔。Swift 在字串(String)的宣告是直覺式的,僅以 firstMessage = "Swift is awesome." 表達。但,Objective-C 卻需用很繞口的符號來完成一件事。例如:
NSString *firstMessage = @"Swift is awesome.";
在字串前面用 * 符號 (*firstMessage) 對初學者而言會問:這是什麼玩意啊?殊不知這是 C 語言的餘孽。在 C 裏頭,符號 * 代表指針(pointer),所以 firstMessage 儲存的是指向 "Swift is awesome." 這一串值的位置。
讀到此,是不是有點頭痛?學 C 的功力好壞就看 Pointer 是否運用自如得體!
第四:字串的串接 (Concat) 上,Swift 是大大優於 Objective-C。後者對初學者而言看似用了奇怪的語法。2 個字串的串接在直覺上不是“第一個字串” 串接 (concat) "第二個字串“就好了嗎?至於串接(concat)這指令是用加號(+)或是其他符號都可。
字串的操作(插入/刪除/修改某字元/字串)在 Objective-C 較為複雜。被宣告為 NSString 的字串是常數字串,不能變更,必須透過 NSMutableString 宣告為變數字串才行。對 Swift 而言就沒有這麼麻煩,字串是否為常數或變數只是由 let 或 var 的宣告定義。
最後,在字串的顯示上,Objective-C 需定義顯示字串的格式 (print format),而 Swift 則由其編譯器去推論判斷。
結論:
Swift 的簡潔語法可大大增加程式的開發速度,同時在程式碼維護的易讀 (Readable) 也有加分的作用。用 Swift 編譯出的執行檔 executable code,根據官方說法,其執行效能又優於 Objective-C。Swift 這種種的優點讓程式開發者不心動也難 ?!
/end
2017年6月21日 星期三
[Swift 3] Error: unrecognized selector sent to instance
Problem
self.present(alertController, animated: true, completion: nil)
當 compiling 時,出現下列 error:
2017-06-21 11:04:57.574 HelloWorlDemo[2244:87318] -[HelloWorlDemo.ViewController helloWorldBtn:]: unrecognized selector sent to instance 0x7fc2d2e0f110
2017-06-21 11:04:57.574 HelloWorlDemo[2244:87318] -[HelloWorlDemo.ViewController helloWorldBtn:]: unrecognized selector sent to instance 0x7fc2d2e0f110
2017-06-21 11:04:57.586 HelloWorlDemo[2244:87318] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[HelloWorlDemo.ViewController helloWorldBtn:]: unrecognized selector sent to instance 0x7fc2d2e0f110'
*** First throw call stack:
(
0 CoreFoundation 0x000000010ba65b0b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000108df5141 objc_exception_throw + 48
2 CoreFoundation 0x000000010bad5134 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x000000010b9ec840 ___forwarding___ + 1024
4 CoreFoundation 0x000000010b9ec3b8 _CF_forwarding_prep_0 + 120
5 UIKit 0x00000001092c8d82 -[UIApplication sendAction:to:from:forEvent:] + 83
6 UIKit 0x000000010944d5ac -[UIControl sendAction:to:forEvent:] + 67
7 UIKit 0x000000010944d8c7 -[UIControl _sendActionsForEvents:withEvent:] + 450
8 UIKit 0x000000010944c802 -[UIControl touchesEnded:withEvent:] + 618
9 UIKit 0x00000001093367ea -[UIWindow _sendTouchesForEvent:] + 2707
10 UIKit 0x0000000109337f00 -[UIWindow sendEvent:] + 4114
11 UIKit 0x00000001092e4a84 -[UIApplication sendEvent:] + 352
12 UIKit 0x0000000109ac85d4 __dispatchPreprocessedEventFromEventQueue + 2926
13 UIKit 0x0000000109ac0532 __handleEventQueue + 1122
14 CoreFoundation 0x000000010ba0bc01 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
15 CoreFoundation 0x000000010b9f10cf __CFRunLoopDoSources0 + 527
16 CoreFoundation 0x000000010b9f05ff __CFRunLoopRun + 911
17 CoreFoundation 0x000000010b9f0016 CFRunLoopRunSpecific + 406
18 GraphicsServices 0x000000010d970a24 GSEventRunModal + 62
19 UIKit 0x00000001092c7134 UIApplicationMain + 159
20 HelloWorlDemo 0x000000010881d9e7 main + 55
21 libdyld.dylib 0x000000010ca0565d start + 1
22 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Solution
Swift 3.0 已改版,需將參數 animated 設定值為 false 即可:
self.present(alertController, animated: false, completion: nil)
2017年5月9日 星期二
範例:豆漿食譜
Objective
製作豆漿時,豆子與水的比例相當重要,此範例提供方便計算,而無需人為的換算UI
事實上,此 UI 僅提供簡單設計,重點在強調功能面。有關各 UI Component 日後再調整。Code
//
// ViewController.swift
// FoodRecipeUnit
//
// Created by Elvis Meng on 2017/5/8.
// Copyright © 2017年 Elvis Meng. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
var soyBeanUnit = ["公克","兩"]
var waterUnit = ["瓢","升"]
var sugarUnit = ["公克","兩"]
var brixUnit = ["微甜","甜"]
var soyBeanUnitSelected = "公克"
var waterUnitSelected = "瓢"
var sugarUnitSelected = "公克"
var brixUnitSelected = "微甜"
var soyBeanAmount = 0.0
var waterAmount = 0.0
var sugarAmount = 0.0
@IBOutlet weak var soyBeanWeight: UITextField!
@IBOutlet weak var waterWeight: UITextField!
@IBOutlet weak var sugarWeight: UITextField!
@IBAction func soyBeanEditOnExit(_ sender: UITextField) {
if soyBeanUnitSelected == "兩" {
soyBeanAmount = 600.0 * Double(soyBeanWeight.text!)!
} else {
soyBeanAmount = Double(soyBeanWeight.text!)!
}
if waterUnitSelected == "瓢" {
waterWeight.text = String(soyBeanAmount * 7.0 / 1500.0)
} else {
waterWeight.text = String(soyBeanAmount * 7.0 * 2500 / 1500.0)
}
}
@IBAction func waterEditOnExit(_ sender: UITextField) {
if waterUnitSelected == "瓢" {
waterAmount = 2500.0 * Double(waterWeight.text!)!
} else {
waterAmount = Double(waterWeight.text!)!
}
if soyBeanUnitSelected == "兩" {
soyBeanWeight.text = String(waterAmount * 2.5 / (7.0 * 2500.0))
} else {
soyBeanWeight.text = String(waterAmount * 2.5 / (7.0 * 2500.0) * 600.0)
}
}
@IBAction func calcBtn(_ sender: UIButton) {
soyBeanWeight.text = nil
waterWeight.text = nil
sugarWeight.text = nil
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch pickerView.tag {
case 0:
return soyBeanUnit.count
case 1:
return waterUnit.count
case 2:
return sugarUnit.count
case 3:
return brixUnit.count
default:
return 0
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
switch pickerView.tag {
case 0:
soyBeanUnitSelected = soyBeanUnit[row]
return soyBeanUnit[row]
case 1:
waterUnitSelected = waterUnit[row]
return waterUnit[row]
case 2:
sugarUnitSelected = sugarUnit[row]
return sugarUnit[row]
case 3:
brixUnitSelected = brixUnit[row]
return brixUnit[row]
default:
return "error"
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Test
Summary
1. 此範例不實作糖與甜度,此部分可留待日後實作2. 在 textField 中,輸入資料,按下 enter,會啟動 EditingOnExit 事件,可自動帶出換算結果,而 UI 可省去 Calculate 這個 Button
Reference
1. UITextField, https://developer.apple.com/reference/uikit/uitextfield2. UIViewController, https://developer.apple.com/reference/uikit/uiviewcontroller
/end
2017年5月5日 星期五
範例:簡單型計算機
Objective
此實作參考 [1],而試著將 Objective-C 程式 Port 到 Swift 3.0 程式
Lab
1. 新增 Single View Application 專案
2. UI 設計
Code
1. File Calculation.swift
//
// Calculation.swift
// myCaculatorDemo
//
// Created by Elvis Meng on 2017/5/4.
// Copyright © 2017年 Elvis Meng. All rights reserved.
//
import Foundation
class Calculation : NSObject {
var operandA : Float?
var operandB : Float?
var op : Character?
var isFirstOperand : Bool
var result : Float?
override init() {
operandA = 0.0
operandB = 0.0
op = nil
isFirstOperand = true
result = 0.0
}
func calculateResult(operandA : Float, operandB : Float, op : Character) -> Float {
switch op {
case "+" :
result = operandA + operandB
case "-" :
result = operandA - operandB
case "*" :
result = operandA * operandB
case "/" :
result = operandA / operandB
default :
print("error")
}
return result!
}
}
2. File : ViewController.swift
//
// ViewController.swift
// myCaculatorDemo
//
// Created by Elvis Meng on 2017/4/25.
// Copyright © 2017年 Elvis Meng. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var displayResult: UILabel!
var cal:Calculation = Calculation()
var isFirstDigit = true
var hasTapEqual = false
var operand:Float = 0.0
var result:Float = 0.0
var digitCount = 0
var digit = 0
func processCalc(op:Character){
if (cal.isFirstOperand){
cal.operandA = operand
cal.isFirstOperand = false
}
else if (hasTapEqual){
cal.operandA = result
cal.operandB = operand
hasTapEqual = false
}
else {
cal.operandB = operand
result = cal.calculateResult(operandA: cal.operandA!, operandB: cal.operandB!, op: op)
displayProcess(num: result)
cal.operandA = result
}
cal.op = op
operand = 0.0
digitCount = 0
}
@IBAction func tapDigit(_ sender: UIButton) {
digit = sender.tag
if hasTapEqual == true {
displayResult.text = "0.0"
cal.operandA = 0
cal.operandB = 0
cal.isFirstOperand = true
result = 0
operand = 0
isFirstDigit = true
hasTapEqual = false
}
if (isFirstDigit && digit == 0){
isFirstDigit = true
} else {
if (digitCount >= 15) {
return
}
isFirstDigit = false
operand = operand * 10 + Float(digit)
displayProcess(num: operand)
}
digitCount += 1
}
@IBAction func tapPlus(_ sender: UIButton) {
processCalc(op: "+")
}
@IBAction func tapMinus(_ sender: UIButton) {
processCalc(op: "-")
}
@IBAction func tapMultiply(_ sender: UIButton) {
processCalc(op: "*")
}
@IBAction func tapDivide(_ sender: UIButton) {
processCalc(op: "/")
}
@IBAction func tapEqual(_ sender: UIButton) {
if (cal.isFirstOperand == false){
cal.operandB = operand
result = cal.calculateResult(operandA: cal.operandA!, operandB: cal.operandB!, op: cal.op!)
self.displayProcess(num: result)
cal.operandA = result
hasTapEqual = true
}
}
@IBAction func tapAC(_ sender: UIButton) {
displayResult.text = "0.0"
cal.operandA = 0
cal.operandB = 0
cal.isFirstOperand = true
result = 0
operand = 0
isFirstDigit = true
hasTapEqual = false
}
func displayProcess(num:Float){
displayResult.text = String(num)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let cal:Calculation = Calculation()
cal.isFirstOperand = true
isFirstDigit = true
hasTapEqual = false
displayResult.text = String(0.0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Test
Reference
1. Chapter 24, 範例程式五:簡易計算器實作, P24-27, “學會Objective-C 的24堂課“, 蔡明志
/end
範例:撥打電話
Objective
學習使用 Button 元件,同時處理多按鈕共用事件
Lab
1. 建立 Single View Application
2. UI 設計
在此畫面需加入 2 張 image,一個作為 Phone Call,另一個為 Phone Hangup
Code
//
// ViewController.swift
// MyPhoneDemo
//
// Created by Elvis Meng on 2017/5/5.
// Copyright © 2017年 Elvis Meng. All rights reserved.
//
import UIKit
var str:String = ""
class ViewController: UIViewController {
@IBOutlet weak var displayPhoneNumber: UILabel!
@IBAction func tapDigitPad(_ sender: UIButton) {
str = str + (sender.titleLabel?.text)!
displayPhoneNumber.text = str
}
@IBAction func phoneCall(_ sender: UIButton) {
let url = URL(string: "tel:"+str)
if #available(iOS 10.0, *) {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url!)
}
}
@IBAction func phoneHangUp(_ sender: UIButton) {
str = ""
displayPhoneNumber.text = ""
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Test
Summary
此範例並處理 Phone Call,也處理 Phone Up
/end
2017年4月27日 星期四
Error: value of type '[String]' has no member 'removeValueForKey'
Problem
以 removeAtIndex 刪除 Array 中一個 Element,出現下列錯誤:
Solution
Swift 3 不支援 removeAtIndex 刪除 Array 中的一個 Element,而改用:
2017年4月20日 星期四
iOS 框架 Framework
iOS 可以分類為 4 個層別 Layer。即:Cocoa Touch,Media 媒體,Core Service 核心服務,以及 Core OS 核心作業系統。[1]
Cocoa Touch 提供使用者控制介面基本物件,例如:按鈕 Button,標籤 Label 等。( UIKit,MapKit,GameKit,MessageUI / AddressBookUI / EventKitUI, Twitter,iAd)
Media 層負責圖像,影音播放,3D 圖像生成等。(AVFoundation ,CoreAudio,CoreImage,CoreGraphics,CoreText,ImageI/O,MediaPlayer,OpenGL ES,QuartzCore)
Core Services 層用來存取較低階的作業系統服務,例如檔案存取,網路,以及許多資料物件類型,我們可透過基礎套件 Foundation 來定義自根類別 NSObject 的所有物件。基礎套件也定義了建立,管理,以及在記憶體中釋放物件的協定 Protocol。(Accounts, AddressBook, CFNetwork, CoreData, CoreFoundation, Foundation, EventKit, CoreLocation, CoreMotion, QuickLook, StoreKit,System Configuration )
Core OS 層包含執行緒,複雜的數學,硬體配件及密碼學等。(Accelerate, ExtterAccessory,Security,System)
框架 Framework 是個工具箱 Tool Box,此工具箱由各種類別程式庫 Class Library 組成,作為開發 App 的基本套件。
Advanced Study
1. Cocoa 基礎指南:2. iOS 人機界面指南
Reference
1. https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/OSX_Technology_Overview/CoreServicesLayer/CoreServicesLayer.html
2. Cocoa Layered Architecture for Mac OS, http://www.knowstack.com/cocoa-layered-architecture-for-mac-osx/
/end
訂閱:
文章 (Atom)








