prettyprint

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/uitextfield
2. 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

2017年3月24日 星期五

練習 3:自訂 Table View

1. 新建 Single View Application,之後 delete 此 default 之 UIViewController 與 ViewController.swift 檔案

2. 在 project 的資料夾,新建 New File > Cocoa Touch Class

3. 將此新增 class 與 UI 整合:指定此UI的TableView 的 class 名稱為 WordsTableViewController


4. 指定此為 First Initial Response

5.  將 TableView 的 Style = Basic, Cell Identifier = Cell

6.  修改 code

//
//  WordsTableViewController.swift
//  MyWords
//
//  Created by Elvis Meng on 2017/3/24.
//  Copyright © 2017 Elvis Meng. All rights reserved.
//

import UIKit

class WordsTableViewController: UITableViewController {
    
    var objNames = ["obj1","obj2","obj3","obj4","obj5","obj6","obj7","obj8","obj9","obj10","obj11","obj12","obj13","obj14","obj15","obj16","obj17","obj18","obj19","obj20"]

    override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem()
    }

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

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return objNames.count
    }

    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)

        // Configure the cell...
        cell.textLabel?.text = objNames[indexPath.row]

        return cell
    }
    

    /*
    // Override to support conditional editing of the table view.
    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return true
    }
    */

    /*
    // Override to support editing the table view.
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // Delete the row from the data source
            tableView.deleteRows(at: [indexPath], with: .fade)
        } else if editingStyle == .insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }    
    }
    */

    /*
    // Override to support rearranging the table view.
    override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

    }
    */

    /*
    // Override to support conditional rearranging of the table view.
    override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        // Return false if you do not want the item to be re-orderable.
        return true
    }
    */

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */


}

7. 告知 data source 的來源


8. 若要加入 image,只要將 image 拖曳至 Access.xcassets 資料夾,然後修改下列 code:


var objNames = ["人間失格","obj2","obj3","obj4","obj5","obj6","obj7","obj8","obj9","obj10","obj11","obj12","obj13","obj14","obj15","obj16","obj17","obj18","obj19","obj20"]

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)

        // Configure the cell...
        cell.textLabel?.text = objNames[indexPath.row]
        cell.imageView?.image = UIImage(named: objNames[indexPath.row])

        return cell
    }

測試

/end

練習 2: Simple Table View [Swift 3.0]


1. 新增 Single Table View Project

2. 拖曳 Table View 物件至 View Controller,並設定 Prototype Cell 的值為 1


3. 點選 Table View Cell,設定其 Identifier 的值為 Cell,Style = Basic


4. UITableView 採用 UITableViewDataSource 與 UITableViewDelegate 兩個協定 Protocol


class ViewController: UIViewController, UITableViewDelegate, UITableViewDelegate {}

6. 關於 UITableViewDataSource,我們必須實做的方法:

tableView(_:numberOfRowsInSection:)
tableView(_:cellForRowAtIndexPath:)

完整 ViewController.swift 如下:

//
//  ViewController.swift
//  MySimpleTableViewDemo
//
//  Created by Elvis Meng on 2017/3/23.
//  Copyright © 2017 Elvis Meng. All rights reserved.
//

import UIKit

var objNames = ["obj1","obj2","obj3","obj4","obj5","obj6","obj7","obj8","obj9","obj10","obj11","obj12","obj13","obj14","obj15","obj16","obj17","obj18","obj19","obj20"]

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    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.
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section:Int) -> Int {
        return objNames.count
    }

    func tableView(_ tableView:UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
        cell.textLabel?.text = objNames[indexPath.row]
        
        return cell
    }
}



7. 整合 UI / Code,將 UI 的 dataSource / delegate 的連接點



8. 測試

點選滑鼠右鍵,然後下拉 scroll down


若要在每個 cell 中顯示圖片 image:

1. 將圖片 image 拖曳至 Access.xcassets 目錄夾中



2. 在 Image View 屬性中,設定 Image 的值為 “英文字母“,此時這 image 加入至 Content View 中 ( 因為 Cell  的 style 為 Basic,其涵 Image View)


3. 修改 Code

import UIKit

var objNames = ["obj1","obj2","obj3","obj4","obj5","obj6","obj7","obj8","obj9","obj10","obj11","obj12","obj13","obj14","obj15","obj16","obj17","obj18","obj19","obj20"]

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    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.
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section:Int) -> Int {
        return objNames.count
    }

    func tableView(_ tableView:UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
        cell.textLabel?.text = objNames[indexPath.row]
        cell.imageView?.image = UIImage(named: "英文字母")
        
        return cell
    }

}


4. 測試


若想每個 Cell 正確顯示不同的 image,需更改下列 code。別忘了 objNames 中的名稱必須與 image 的命名一致。


var objNames = ["英文字母","英文字母","obj3","obj4","obj5","obj6","obj7","obj8","obj9","obj10","obj11","obj12","obj13","obj14","obj15","obj16","obj17","obj18","obj19","obj20"]

func tableView(_ tableView:UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
        cell.textLabel?.text = objNames[indexPath.row]
        cell.imageView?.image = UIImage(named: objNames[indexPath.row])
        
        return cell
    }

測試:



/end
prettyPrint();