2015年12月8日 星期二

iOS筆記:Swift (2)

Collection Types

Swift 語言提供Arrays、Sets和Dictionaries三種基本的集合類型用來存儲集合數據。數組(Arrays)是有序數據的集。集合(Sets)是無序無重複數據的集。字典(Dictionaries)是無序的鍵值對的集。

enter image description here

Arrays

Arrays使用有序列表存儲同一類型的多個值。相同的值可以多次出現在一個數組的不同位置中。
The type of a Swift array is written in full as Array, where Element is the type of values the array is allowed to store.

Note that the type of the someInts variable is inferred to be [Int] from the type of the initializer.
ex. Creating an Empty Array:

    var someInts = [Int]()
    print("someInts is of type [Int] with \(someInts.count) items.")
    // prints "someInts is of type [Int] with 0 items."

    someInts.append(3)
        // someInts 現在包含一個 Int 值
        someInts = []
        // someInts 現在是空數組,但是仍然是[Int] 類型的。

ex. Creating an Array with a Default Value
Pass this initializer the number of items to be added to the new array (called count) and a default value of the appropriate type (called repeatedValue):

    var threeDoubles = [Double](count: 3, repeatedValue: 0.0)
    // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]

    //Creating an Array by Adding Two Arrays Together
    var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.5) // [2.5, 2.5, 2.5]

    var sixDoubles = threeDoubles + anotherThreeDoubles
    // sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

ex. Creating an Array with an Array Literal

    var shoppingList: [String] = ["Eggs", "Milk"]
    // shoppingList has been initialized with two initial items

    for item in shoppingList {
    print(item)
}
// Eggs
// Milk

enumerate()返回一個由每一個數據項索引值和數據值組成的元組。

for (index, value) in shoppingList.enumerate() {
    print("Item \(index + 1): \(value)")
}
// Item 1: Eggs
// Item 2: Milk

Sets

Set用來存儲相同類型並且沒有確定順序的值。當集合元素順序不重要時或者希望確保每個元素只出現一次時可以使用集合而不是數組。

Swift’s Set type is bridged to Foundation’s NSSet class.

Hash Values for Set Types

A type must be hashable in order to be stored in a set—that is, the type must provide a way to compute a hash value for itself. A hash value is an Int value that is the same for all objects that compare equally, such that if a == b, it follows that a.hashValue == b.hashValue.

Set Type Syntax

Swift 中的Set類型被寫為Set,這裡的Element表示Set中允許存儲的類型,和數組不同的是,集合沒有等價的簡化形式。
ex. Creating and Initializing an Empty Set

    var letters = Set<Character>()
    print("letters is of type Set<Character> with \(letters.count) items.")
    // prints "letters is of type Set<Character> with 0 items."

    letters.insert("a")
    // letters now contains 1 value of type Character
    letters = []
    // letters is now an empty set, but is still of type Set<Character>

ex. Creating a Set with an Array Literal

    var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
    // favoriteGenres has been initialized with three initial items

Accessing and Modifying a Set

    print("I have \(favoriteGenres.count) favorite music genres.")
    // prints "I have 3 favorite music genres."

    if favoriteGenres.isEmpty {
        print("As far as music goes, I'm not picky.")
    } else {
        print("I have particular music preferences.")
    }
    // prints "I have particular music preferences."

a. Add a new item into a set by calling the set’s insert(_:) method.
b. Remove an item from a set by calling the set’s remove(_:) method.
c. To check whether a set contains a particular item, use the contains(_:) method.

Fundamental Set Operations

The illustration below depicts two sets–a and b– with the results of various set operations represented by the shaded regions.

enter image description here

a. 使用intersect(_:)方法根據兩個集合中都包含的值創建的一個新的集合。
b. 使用exclusiveOr(_:)方法根據在一個集合中但不在兩個集合中的值創建一個新的集合。
c. 使用union(_:)方法根據兩個集合的值創建一個新的集合。
d. 使用subtract(_:)方法根據不在該集合中的值創建一個新的集合。

   let oddDigits: Set = [1, 3, 5, 7, 9]
    let evenDigits: Set = [0, 2, 4, 6, 8]
    let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]

    oddDigits.union(evenDigits).sort()
    // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    oddDigits.intersect(evenDigits).sort()
    // []
    oddDigits.subtract(singleDigitPrimeNumbers).sort()
    // [1, 9]
    oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
    // [1, 2, 9]

Set Membership and Equality

    let houseAnimals: Set = ["dog", "cat"]
    let farmAnimals: Set = ["cow", "chicken", "Sheep", "dog", "cat"]
    let cityAnimals: Set = ["pigeon", "rat"]

    houseAnimals.isSubsetOf(farmAnimals)
    // true
    farmAnimals.isSupersetOf(houseAnimals)
    // true
    farmAnimals.isDisjointWith(cityAnimals)
    // true

Dictionaries

字典是一種存儲多個相同類型的值的容器。每個值(value)都關聯唯一的key.

Swift’s Dictionary type is bridged to Foundation’s NSDictionary class.

Dictionary Type Shorthand Syntax

Swift 的字典使用Dictionary<Key, Value>定義,其中Key是字典中鍵的數據類型,Value是字典中對應於這些鍵所存儲值的數據類型。也可以用[Key: Value]怎樣快捷的形式去創建一個字典類型。

Creating a Dictionary

    var namesOfIntegers = [Int: String]()
    // namesOfIntegers is an empty [Int: String] dictionary

    namesOfIntegers[16] = "sixteen"
    // namesOfIntegers now contains 1 key-value pair
    namesOfIntegers = [:]
    // namesOfIntegers is once again an empty dictionary of type [Int: String]

一個鍵值對是一個key和一個value的結合體。在字典字面量中,每一個鍵值對的鍵和值都由冒號分割。這些鍵值對構成一個列表,其中這些鍵值對由方括號包含、由逗號分割:
[key 1: value 1, key 2: value 2, key 3: value 3]

    var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

也可以使用以下宣告方式,Swift 可以推斷出Dictionary<String, String>是airports字典的正確類型。

    var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

Accessing and Modifying a Dictionary

大部分的用法同上述,The updateValue(_:forKey:) method returns an optional value of the dictionary’s value type.remove ValueForKey(_:)方法也可以用來在字典中移除鍵值對。這個方法在鍵值對存在的情況下會移除該鍵值對並且返回被移除的值或者在沒有值的情況下返回nil:

    if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
        print("The old value for DUB was \(oldValue).")
    }
    // prints "The old value for DUB was Dublin."


    if let removedValue = airports.removeValueForKey("DUB") {
        print("The removed airport's name is \(removedValue).")
    } else {
        print("The airports dictionary does not contain a value for DUB.")
    }
    // prints "The removed airport's name is Dublin Airport."

FOR-IN method 1.

    for (airportCode, airportName) in airports {
        print("\(airportCode): \(airportName)")
    }
    // YYZ: Toronto Pearson
    // LHR: London Heathrow

Method 2.

    for airportCode in airports.keys {
        print("Airport code: \(airportCode)")
    }
    // Airport code: YYZ
    // Airport code: LHR

    for airportName in airports.values {
        print("Airport name: \(airportName)")
    }
    // Airport name: Toronto Pearson
    // Airport name: London Heathrow

Forloop

Swift 提供兩種for循環形式以來按照指定的次數多次執行一系列語句:

  • for-in循環對一個集合裡面的每個元素執行一系列語句。
  • for 循環,用來重複執行一系列語句直到達成特定條件達成,一般通過在每次循環完成後增加計數器的值來實現。

For-In

Example:

    for index in 1...5 {
        print("\(index) times 5 is \(index * 5)")
    }
    // 1 times 5 is 5
    // 2 times 5 is 10
    // 3 times 5 is 15
    // 4 times 5 is 20
    // 5 times 5 is 25

如果不需要知道區間序列內每一項的值,你可以使用下劃線(_)替代變量名來忽略對值的訪問:

    let base = 3
    let power = 10
    var answer = 1
    for _ in 1...power {
        answer *= base
    }
    print("\(base) to the power of \(power) is \(answer)")
    // prints "3 to the power of 10 is 59049"
    let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
    for (animalName, legCount) in numberOfLegs {
        print("\(animalName)s have \(legCount) legs")
    }
    // ants have 6 legs
    // cats have 4 legs
    // spiders have 8 legs

Swift supports traditional C-style for loops with a condition and an incrementer:

    for var index = 0; index < 3; ++index {
        print("index is \(index)")
    }
    // index is 0
    // index is 1
    // index is 2

While Loops

while循環運行一系列語句直到條件變成false。這類循環適合使用在第一次迭代前迭代次數未知的情況下。 Swift 提供兩種while循環形式:

  • while循環,每次在循環開始時計算條件是否符合;
  • repeat-while循環,每次在循環結束時計算條件是否符合。

while循環從計算單一條件開始。如果條件為true,會重複運行一系列語句,直到條件變為false。

    while condition {
        statements
    }
        var square = 0
        var diceRoll = 0
        while square < finalSquare {
            // roll the dice
            if ++diceRoll == 7 { diceRoll = 1 }
            // move by the rolled amount
           square += diceRoll
          if square < board.count {
                // if we're still on the board, move up or down for a snake or a ladder
                square += board[square]
            }
        }

while循環的另外一種形式是repeat-whil​​e,它和while的區別是在判斷循環條件之前,先執行一次循環的代碼塊,然後重複循環直到條件為false。

The repeat-while loop in Swift is analogous to a do-while loop in other languages.

        repeat {
            statements
        } while condition
    repeat {
        // move up or down for a snake or ladder
        square += board[square]
        // roll the dice
        if ++diceRoll == 7 { diceRoll = 1 }
        // move by the rolled amount
        square += diceRoll
    } while square < finalSquare
    print("Game over!")

Conditional Statements

if-else

    temperatureInFahrenheit = 90
    if temperatureInFahrenheit <= 32 {
        print("It's very cold. Consider wearing a scarf.")
    } else if temperatureInFahrenheit >= 86 {
        print("It's really warm. Don't forget to wear sunscreen.")
    } else {
        print("It's not that cold. Wear a t-shirt.")
    }
    // prints "It's really warm. Don't forget to wear sunscreen."

Switch
switch語句會嘗試把某個值與若干個模式(pattern)進行匹配。根據第一個匹配成功的模式,switch語句會執行對應的代碼。當有可能的情況較多時,通常用switch語句替換if語句。

No Implicit Fallthrough
與C 語言和Objective-C 中的switch語句不同,在Swift 中,當匹配的case 分支中的代碼執行完畢後,程序會終止switch語句,而不會繼續執行下一個case 分支。這也就是說,不需要在case 分支中顯式地使用break語句。這使得switch語句更安全、更易用,也避免了因忘記寫break語句而產生的錯誤。

Although break is not required in Swift, you can still use a break statement to match and ignore a particular case, or to break out of a matched case before that case has completed its execution.

    let anotherCharacter: Character = "a"
    switch anotherCharacter {
    case "a":
    case "A":
        print("The letter A")
    default:
        print("Not the letter A")
    }
    // this will report a compile-time error

case 分支的模式也可以是一個值的區間。

    let approximateCount = 62
    let countedThings = "moons orbiting Saturn"
    var naturalCount: String
    switch approximateCount {
    case 0:
        naturalCount = "no"
    case 1..<5: 
        naturalCount = "a few"
    case 5..<12:
        naturalCount = "several"
    case 12..<100:
        naturalCount = "dozens of"
    case 100..<1000:
        naturalCount = "hundreds of"
    default:
        naturalCount = "many"
    }
    print("There are \(naturalCount) \(countedThings).")
    // prints "There are dozens of moons orbiting Saturn."

使用Tuple在同一個switch語句中測試多個值。Tuple中的元素可以是值,也可以是區間。另外,使用下劃線(_)來匹配所有可能的值。

    let somePoint = (1, 1)
    switch somePoint {
    case (0, 0):
        print("(0, 0) is at the origin")
    case (_, 0):
        print("(\(somePoint.0), 0) is on the x-axis")
    case (0, _):
        print("(0, \(somePoint.1)) is on the y-axis")
    case (-2...2, -2...2):
        print("(\(somePoint.0), \(somePoint.1)) is inside the box")
    default:
        print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
    }
    // prints "(1, 1) is inside the box"

Where
case 分支的模式可以使用where語句來判斷額外的條件。

    let yetAnotherPoint = (1, -1)
    switch yetAnotherPoint {
    case let (x, y) where x == y:
        print("(\(x), \(y)) is on the line x == y")
    case let (x, y) where x == -y:
        print("(\(x), \(y)) is on the line x == -y")
    case let (x, y):
        print("(\(x), \(y)) is just some arbitrary point")
    }
    // prints "(1, -1) is on the line x == -y"

Control Transfer Statements

  1. continue語句告訴一個循環體立刻停止本次循環迭代,重新開始下次循環迭代。就好像在說“本次循環迭代我已經執行完了”,但是並不會離開整個循環體。
    let puzzleInput = "great minds think alike"
    var puzzleOutput = ""
    for character in puzzleInput.characters {
        switch character {
        case "a", "e", "i", "o", "u", " ":
            continue
        default:
            puzzleOutput.append(character)
        }
    }
    print(puzzleOutput)
    // prints "grtmndsthnklk"
  1. break語句會立刻結束整個控制流的執行。
    當在一個循環體中使用break時,會立刻中斷該循環體的執行,然後跳轉到表示循環體結束的大括號(})後的第一行代碼。不會再有本次循環迭代的代碼被執行,也不會再有下次的循環迭代產生。

  2. Fallthrough
    這個例子定義了一個String類型的變量description並且給它設置了一個初始值。函數使用switch邏輯來判斷integerToDescribe變量的值。當integerToDescribe的值屬於列表中的質數之一時,該函數添加一段文字在description後,來表明這個是數字是一個質數。然後它使用fallthrough關鍵字來“貫穿”到default分支中。 default分支添加一段額外的文字在description的最後,至此switch代碼塊執行完了。

    let integerToDescribe = 5
    var description = "The number \(integerToDescribe) is"
    switch integerToDescribe {
    case 2, 3, 5, 7, 11, 13, 17, 19:
        description += " a prime number, and also"
        fallthrough
    default:
        description += " an integer."
    }
    print(description)
    // prints "The number 5 is a prime number, and also an integer."

The fallthrough keyword does not check the case conditions for the switch case that it causes execution to fall into. The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block, as in C’s standard switch statement behavior.

  1. Early Exit
    像if語句一樣,guard的執行取決於一個表達式的 Boolean value。一個guard語句總是有一個else分句,如果條件不為真則執行else分句中的代碼。
    func greet(person: [String: String]) {
        guard let name = person["name"] else {
            return
        }     
        print("Hello \(name)!")

        guard let location = person["location"] else {
            print("I hope the weather is nice near you.")
            return
        }

        print("I hope the weather is nice in \(location).")
    }

    greet(["name": "John"])
    // prints "Hello John!"
    // prints "I hope the weather is nice near you."
    greet(["name": "Jane", "location": "Cupertino"])
    // prints "Hello Jane!"
    // prints "I hope the weather is nice in Cupertino."

Checking API Availability

Swift has built-in support for checking API availability, which ensures that you don’t accidentally use APIs that are unavailable on a given deployment target.

    if #available(iOS 9, OSX 10.10, *) {
        // Use iOS 9 APIs on iOS, and use OS X v10.10 APIs on OS X
    } else {
        // Fall back to earlier iOS and OS X APIs
    }

Defining and Calling Functions

在下面例子中的函數叫做”sayHello(_:)”,這個函數用一個人的名字當做輸入,並返回給這個人的問候語。為了完成這個任務,你定義一個輸入參數-一個叫做personName 的String 值,和一個包含給這個人問候語的String 類型的返回值:

    func sayHello(personName: String) -> String {
        let greeting = "Hello, " + personName + "!"
        return greeting
    }

    print(sayHello("Anna"))
    // prints "Hello, Anna!"

Functions Without Parameters

儘管這個函數沒有參數,但是定義中在函數名後還是需要一對圓括號。當被調用時,也需要在函數名後寫一對圓括號。

    func sayHelloWorld() -> String {
        return "hello, world"
    }
    print(sayHelloWorld())
    // prints "hello, world"

Functions With Multiple Parameters

    func sayHello(personName: String, alreadyGreeted: Bool) -> String {
        if alreadyGreeted {
            return sayHelloAgain(personName)
        } else {
            return sayHello(personName)
        }
    }
    print(sayHello("Tim", alreadyGreeted: true))
    // prints "Hello again, Tim!"

Functions Without Return Values

    func printAndCount(stringToPrint: String) -> Int {
        print(stringToPrint)
        return stringToPrint.characters.count
    }
    func printWithoutCounting(stringToPrint: String) {
        printAndCount(stringToPrint)
    }
    printAndCount("hello, world")
    // prints "hello, world" and returns a value of 12
    printWithoutCounting("hello, world")
    // prints "hello, world" but does not return a value

Functions with Multiple Return Values

You can use a tuple type as the return type for a function to return multiple values as part of one compound return value.

    func minMax(array: [Int]) -> (min: Int, max: Int) {
        var currentMin = array[0]
        var currentMax = array[0]
        for value in array[1..<array.count] {
            if value < currentMin {
                currentMin = value
            } else if value > currentMax {
                currentMax = value
            }
        }
        return (currentMin, currentMax)
    }

Optional Tuple Return Types

如果函數返回的元組類型有可能整個元組都“沒有值”,你可以使用可選的(Optional) 元組返回類型反映整個元組可以是nil的事實。

    func minMax(array: [Int]) -> (min: Int, max: Int)? {
        if array.isEmpty { return nil }
        var currentMin = array[0]
        var currentMax = array[0]
        for value in array[1..<array.count] {
            if value < currentMin {
                currentMin = value
            } else if value > currentMax {
                currentMax = value
            }
        }
        return (currentMin, currentMax)
    }

    if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
        print("min is \(bounds.min) and max is \(bounds.max)")
    }
    // prints "min is -6 and max is 109"

Specifying External Parameter Names

        func sayHello(to person: String, and anotherPerson: String) -> String {
            return "Hello \(person) and \(anotherPerson)!"
        }
        print(sayHello(to: "Bill", and: "Ted"))
        // prints "Hello Bill and Ted!"

Default Parameter Values

    func someFunction(parameterWithDefault: Int = 12) {
        // function body goes here
        // if no arguments are passed to the function call,
        // value of parameterWithDefault is 12
    }
    someFunction(6) // parameterWithDefault is 6
    someFunction() // parameterWithDefault is 12

Variadic Parameters

一個可變參數(variadic parameter)可以接受零個或多個值。函數調用時,你可以用可變參數來指定函數參數可以被傳入不確定數量的輸入值。通過在變量類型名後面加入(…)的方式來定義可變參數。

A function may have at most one variadic parameter.

    func arithmeticMean(numbers: Double...) -> Double {
        var total: Double = 0
        for number in numbers {
            total += number
        }
        return total / Double(numbers.count)
    }
    arithmeticMean(1, 2, 3, 4, 5)
    // returns 3.0, which is the arithmetic mean of these five numbers
    arithmeticMean(3, 8.25, 18.75)
    // returns 10.0, which is the arithmetic mean of these three numbers

Constant and Variable Parameters

Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error.

Define variable parameters by prefixing the parameter name with the var keyword:

    func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
        let amountToPad = totalLength - string.characters.count
        if amountToPad < 1 {
            return string
        }
        let padString = String(pad)
        for _ in 1...amountToPad {
            string = padString + string
        }
        return string
    }
    let originalString = "hello"
    let paddedString = alignRight(originalString, totalLength: 10, pad: "-")
    // paddedString is equal to "-----hello"
    // originalString is still equal to "hello"

In-Out Parameters

變量參數,正如上面所述,僅能在函數體內被更改。如果你想要一個函數可以修改參數的值,並且想要在這些修改在函數調用結束後仍然存在,那麼就應該把這個參數定義為輸入輸出參數(In-Out Parameters)。

In-out parameters cannot have default values, and variadic parameters cannot be marked as inout. If you mark a parameter as inout, it cannot also be marked as var or let.

    func swapTwoInts(inout a: Int, inout _ b: Int) {
        let temporaryA = a
        a = b
        b = temporaryA
    }

    var someInt = 3
    var anotherInt = 107
    swapTwoInts(&someInt, &anotherInt)
    print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
    // prints "someInt is now 107, and anotherInt is now 3"

Function Types

Every function has a specific function type, made up of the parameter types and the return type of the function.

    func addTwoInts(a: Int, _ b: Int) -> Int {
        return a + b
    }
    func multiplyTwoInts(a: Int, _ b: Int) -> Int {
        return a * b
    }

這兩個函數的類型是(Int, Int) -> Int,可以解讀為“A function type that has two parameters, both of type Int, and that returns a value of type Int.”。

Using Function Types

You use function types just like any other types in Swift.

    var mathFunction: (Int, Int) -> Int = addTwoInts

This can be read as:

“Define a variable called mathFunction, which has a type of ‘a function that takes two Int values, and returns an Int value.’ Set this new variable to refer to the function called addTwoInts.”

    print("Result: \(mathFunction(2, 3))")
    // prints "Result: 5"

As with any other type, you can leave it to Swift to infer the function type when you assign a function to a constant or variable:

    let anotherMathFunction = addTwoInts
    // anotherMathFunction is inferred to be of type (Int, Int) -> Int

Function Types as Parameter Types

可以用(Int, Int) -> Int這樣的函數類型作為另一個函數的參數類型。這樣你可以將函數的一部分實現留給函數的調用者來提供。

    func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
        print("Result: \(mathFunction(a, b))")
    }
    printMathResult(addTwoInts, 3, 5)
    // prints "Result: 8"

這個例子定義了printMathResult(::_:) 函數,它有三個參數:第一個參數叫mathFunction,類型是(Int, Int) -> Int,你可以傳入任何這種類型的函數;第二個和第三個參數叫a 和b,它們的類型都是Int

Function Types as Return Types

You can use a function type as the return type of another function. You do this by writing a complete function type immediately after the return arrow (->) of the returning function.

    func stepForward(input: Int) -> Int {
        return input + 1
    }
    func stepBackward(input: Int) -> Int {
        return input - 1
    }

Here’s a function called chooseStepFunction(_:), whose return type is “a function of type (Int) -> Int”.

    func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
        return backwards ? stepBackward : stepForward
    }
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function

print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

Nested Functions

到目前為止所見到的所有函數都叫全局函數(global functions),它們定義在全局域中。你也可以把函數定義在別的函數體中,稱作嵌套函數(nested functions)。

嵌套函數是對外界不可見的,但是可以被它們的外圍函數(enclosing function)調用。

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

參考資料:
1. https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0
2. http://wiki.jikexueyuan.com/project/swift/

2015年12月5日 星期六

iOS筆記:UIBezierPath Practice

結果如下圖所示:
enter image description here

規則是當一偵測的手指tap時, 分數就會開始扣. 當超出線的時候就會被扣大量的分數, 當沒有到終點就放棄時遊戲就會停止並跳出警告.

MountainPath

    // First Path

    CGPoint firstPoint = CGPointMake(rect.size.width * (1/6.0), CGRectGetMaxY(rect));
    CGPoint secondPoint = CGPointMake(rect.size.width * (1/3.0), rect.size.height * (5/6.0));
    CGPoint thirdPoint = CGPointMake(CGRectGetMaxX(rect), rect.size.height * (5/6.0));
    CGPoint fourthPoint = CGPointMake(CGRectGetMaxX(rect), rect.size.height * (2/3.0));
    CGPoint fifthPoint = CGPointMake(rect.size.width * (1/6.0), rect.size.height * (6/12.0));
    CGPoint sixthPoint = CGPointMake(rect.size.width * (1/6.0), rect.size.height * (6/12.0));
    CGPoint seventhPoint = CGPointMake(rect.size.width * (1/3.0), rect.size.height * (2/6.0));
    CGPoint eigthPoint = CGPointMake(rect.size.width * (2/3.0), rect.size.height * (6/12.0));
    CGPoint ninthPoint = CGPointMake(rect.size.width * (11/20.0), rect.size.height * (5/24.0));
    CGPoint controlPoint = CGPointMake(rect.size.width * (6 / 8.0), rect.size.height * (1/ 3.0));

UIBezierPath *rightMountainPath = [UIBezierPath bezierPath]; 

[rightMountainPath moveToPoint:firstPoint];
    [rightMountainPath addLineToPoint:secondPoint];
    [rightMountainPath addLineToPoint:thirdPoint];
    [rightMountainPath addLineToPoint:fourthPoint];
    [rightMountainPath addLineToPoint:fifthPoint];
    [rightMountainPath addLineToPoint:sixthPoint];
    [rightMountainPath addLineToPoint:seventhPoint];
    [rightMountainPath addLineToPoint:eigthPoint];
    [rightMountainPath addQuadCurveToPoint:ninthPoint controlPoint:controlPoint];

    [variousPaths addObject:rightMountainPath];

    return [variousPaths copy];

Second Path 則是透過 First Path再去減寬度PATH_WIDTH

畫曲線

  • (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint

endPoint: 曲線的終點
controlPoint: 畫曲線的基準點
enter image description here

Add Tap

+(UIBezierPath *)tapTargetForPath:(UIBezierPath *)path
{
    //To detect that our path is being tapped.
    CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, fmaxf(10.0f, path.lineWidth), path.lineCapStyle, path.lineJoinStyle, path.miterLimit);

    UIBezierPath *tapTarget = [UIBezierPath bezierPathWithCGPath:tapTargetPath];

    CGPathRelease(tapTargetPath);

    return tapTarget;
}

Main View

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)];
    [self.pathView addGestureRecognizer:tapRecognizer];
    //tapRecognizer.numberOfTapsRequired = 2;

    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panDeteced:)];
    [self.pathView addGestureRecognizer:panRecognizer];

    self.scoreLabel.text = [NSString stringWithFormat:@"Score: %i", RTMAP_STARTING_SCORE];
}
- (void)panDeteced:(UIPanGestureRecognizer *)panRecognizer
{
    CGPoint fingerLocation = [panRecognizer locationInView:self.pathView];
    //NSLog(@"I'm at (%f, %f", fingerLocation.x, fingerLocation.y);

    if (panRecognizer.state == UIGestureRecognizerStateBegan && fingerLocation.y < 750) {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:RTTIMER_INTERVAL target:self selector:@selector(timerFired) userInfo:nil repeats:YES];

        self.scoreLabel.text = [NSString stringWithFormat:@"Score: %i", RTMAP_STARTING_SCORE];
    }
    else if (panRecognizer.state == UIGestureRecognizerStateChanged)
    {
        for (UIBezierPath *path in [RTMountainPath mountainPathsForRect:self.pathView.bounds]) {
            UIBezierPath *tapTarget = [RTMountainPath tapTargetForPath:path];

            if ([tapTarget containsPoint:fingerLocation]) {
                //NSLog(@"You hit the wall");
                [self decrementScoreByAmount:RTWALL_PENALTY];
            }
        }
    }
    else if (panRecognizer.state == UIGestureRecognizerStateEnded && fingerLocation.y<= 165)
    {
        [self.timer invalidate];
        self.timer = nil;
    }
    else {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Error" message:@"Make sure to start the bottom of the path, hold your finger down and finish at the top of the path" preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:nil];

        [alertController addAction:cancelAction];
        [alertController addAction:okAction];
        [self presentViewController:alertController animated:YES completion:nil];

        [self.timer invalidate];
        self.timer = nil;
    }
}

- (void)tapDetected:(UITapGestureRecognizer *)tapRecognizer
{
    CGPoint tapLocation = [tapRecognizer locationInView:self.pathView];
    NSLog(@"tap location is at (%f, %f)", tapLocation.x, tapLocation.y);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)timerFired
{
    [self decrementScoreByAmount:RTMAP_SCORE_DECREMENT_AMOUNT];
}

- (void)decrementScoreByAmount:(int)amount
{
    NSString *scoreText = [[self.scoreLabel.text componentsSeparatedByString:@" "] lastObject];
    int score = [scoreText intValue];
    score = score - amount;

    self.scoreLabel.text = [NSString stringWithFormat:@"Score: %i", score];
}

參考:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIBezierPath_class/index.html#//apple_ref/occ/instm/UIBezierPath/addQuadCurveToPoint:controlPoint:

2015年12月2日 星期三

iOS筆記:Swift (1)

常數和變數

The value of a constant cannot be changed once it is set, whereas a variable can be set to a different value in the future.

常數和變數宣告方法

常數和變數必须在使用前宣告,用let來宣告常數,用var來宣告變數。

    let maximumNumberOfLoginAttempts = 10
    var currentLoginAttempt = 0

在上述例子中的概念是,允许的最大嘗試登入次數被宣告為一个常數,因为這個值是固定的不會變。當前嘗試登錄次數被宣告為一個變數,因為每次嘗試登錄失敗的時候都需要增加這個值。

資料型別

Swift中型別按照參數傳遞方式不同可以分為:值型別和參考型別
值型別: 在賦值或把參數傳遞給函式的時候,建立一個參數的副本,把副本傳遞過去,這樣在函式的呼叫過程中不會影響原始資料.

參考型別: 在賦值或把參數傳遞給函式的時候,把本身資料傳遞過去,這樣在函式的呼叫過程中會影響原始資料.

數字型別之間的轉換

Swift對於型別的檢查非常嚴格,不同型別之間不能隨便轉換.

整數之間有兩種轉換方法:
1. 從小範圍數到大範圍數轉換是自動的
2. 從大範圍數到小範圍數需要”強制轉換型別”,有可能造成資料精確度的丟失

Type annotation

當你聲明常量或者變量的時候可以加上類型標註(type annotation),說明常量或者變量中要存儲的值的類型。冒號代表著“是...類型”

    var welcomeMessage: String
    welcomeMessage = "Hello"

在一行中定義多個同樣類型的變量,用逗號分割,並在最後一個變量名之後添加類型標註:

    var red, green, blue: Double

用print(:separator:terminator:)函數來輸出當前常量或變量的值,也可以將常量或變量名放入圓括號中,並在開括號前使用反斜杠將其轉義:

    var friendlyWelcome = "Hello!"
    friendlyWelcome = "Bonjour!"
    print(friendlyWelcome) // 输出 "Bonjour!"

    print("The current value of friendlyWelcome is \(friendlyWelcome)")
    // 输出 "The current value of friendlyWelcome is Bonjour!

The separator and terminator parameter have default values, so you can omit them when you call this function.

Type Aliases

類型別名(typealiases)就是給現有類型定義另一個名字。你可以使用typealias關鍵字來定義類型別名。

    typealias AudioSample = UInt16
    var maxAmplitudeFound = AudioSample.min
    // maxAmplitudeFound 现在是 0    

AudioSample被定義為UInt16的一個別名。因為它是別名,AudioSample.min實際上是UInt16.min,所以會給maxAmplitudeFound賦一個初值0。

Tuples

把多個值組合成一個複合值。元組內的值可以是任意類型,並不要求是相同類型。也可以將一個元組的內容分解(decompose)成單獨的常量和變量,然後你就可以正常使用它們了:

分解的时候可以把要忽略的部分用下標線(_)标记

    let http404Error = (404, "Not Found")
    // http404Error 的類型是 (Int, String),值是 (404, "Not Found")

    let (statusCode, statusMessage) = http404Error
    print("The status code is \(statusCode)")
    // 输出 "The status code is 404"      
    print("The status message is \(statusMessage)")
    // 输出 "The status message is Not Found"

    let (justTheStatusCode, _) = http404Error

Optional Binding

使用optional binding來判斷可選類型是否包含值,如果包含就把值賦給一個臨時常量或者變量。optional binding可以用在if和while語句中,這條語句不僅可以用來判斷可選類型中是否有值,同時可以將可選類型中的值賦給一個常量或者變量。

    let possibleNumber = "123"

    if let actualNumber = Int(possibleNumber) {
    print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
    print("\'\(possibleNumber)\' could not be converted to an integer")
}
    // 输出 "'123' has an integer value of 123"

這段代碼可以被理解為:
“如果Int(possibleNumber)返回的可選Int包含一個值,創建一個叫做actualNumber的新常量並將可選包含的值賦給它。”

++i vs i++

    var a = 0
    let b = ++a // a 和 b 现在都是 1
    let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1

let b = ++a先把a加1了再返回a的值。所以a和b都是新值1。
而let c = a++,是先返回了a的值,然后a才加1。所以c得到了a的旧值1,而a加1后变成2。

Ternary Conditional Operator

Ternary Conditional Operator是以下代码的缩寫形式:

if question {
    answer1
} else {
    answer2
}

ex.

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 现在是 90

Nil Coalescing Operator

The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil.
這個運算式有兩個條件:

  1. a必須是Optional類型
  2. b的類型必須要和a的類型保持一致

Nil Coalescing Operator 是以下的判斷式的縮寫”

a != nil ? a! : b

ex.

let defaultColorName = "red"
var userDefinedColorName: String?   //default值為 nil

var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為nil,所以 colorNameToUse 的值為 "red"

Closed Range Operator and Half-Open Range Operator

Closed Range Operator (a…b)定義一個包含從a到b(包括a和b)的所有值的區間,b必須大於等於a.
用法:

for index in 1...5 {
    print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25

Half-Open Range Operator (a..<b)定義一個從a到b但不包括b的區間.

Initializing an Empty String

要創建一個空字符串作為初始值,可以將空的字符串字面量賦值給變量,也可以初始化一個新的String實例:

    var emptyString = ""               // empty string literal
    var anotherEmptyString = String()  // initializer syntax
    // these two strings are both empty, and are equivalent to each other

可以通過檢查其Boolean類型的isEmpty屬性來判斷該字符串是否為empty:

    if emptyString.isEmpty {
        print("Nothing to see here")
    }
    // prints "Nothing to see here"

Concatenating Strings and Characters

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome = "hello there"

var instruction = "look over"
instruction += string2
// instruction = "look over there"

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome = "hello there!"

String Indices

每一個String值都有一個關聯的索引(index)類型,String.Index,它對應著字符串中的每一個Character的位置.通過調用String.Index的predecessor()方法,可以立即得到前面一個索引,調用successor()方法可以立即得到後面一個索引。

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.endIndex.predecessor()]
// !
greeting[greeting.startIndex.successor()]
// u
let index = greeting.startIndex.advancedBy(7)
greeting[index]
// a

使用characters屬性的indices屬性會創建一個包含全部索引的範圍(Range),用來在一個字符串中訪問單個字符。

for index in greeting.characters.indices {
   print("\(greeting[index]) ", terminator: " ")
}
// 输出 "G u t e n   T a g !"

Inserting and Removing

insert(_:atIndex:)方法可以在一個字符串的指定索引插入一個字符.

var welcome = "hello"
welcome.insert("!", atIndex: welcome.endIndex)
// welcome now = "hello!"

使用insertContentsOf(_:at:)方法可以在一个字符串的指定索引插入一个字符串.

welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor())
// welcome = "hello there!"

removeAtIndex(_:)方法可以在一個字符串的指定索引刪除一個字符.

welcome.removeAtIndex(welcome.endIndex.predecessor())
// welcome = "hello there"

let range = welcome.endIndex.advancedBy(-6)..`<welcome.endIndex
welcome.removeRange(range)
// welcome = "hello"

Prefix and Suffix Equality

通過調用字符串的hasPrefix(_:)/hasSuffix(_:)方法來檢查字符串是否擁有特定前綴/後綴,兩個方法均接收一個String類型的參數,並返回一個布爾值。

    let romeoAndJuliet = [
        "Act 1 Scene 1: Verona, A public place",
        "Act 1 Scene 2: Capulet's mansion",
        "Act 1 Scene 3: A room in Capulet's mansion",
        "Act 1 Scene 4: A street outside Capulet's mansion",
        "Act 1 Scene 5: The Great Hall in Capulet's mansion",
        "Act 2 Scene 1: Outside Capulet's mansion",
        "Act 2 Scene 2: Capulet's orchard",
        "Act 2 Scene 3: Outside Friar Lawrence's cell",
        "Act 2 Scene 4: A street in Verona",
        "Act 2 Scene 5: Capulet's mansion",
        "Act 2 Scene 6: Friar Lawrence's cell"
    ]
    //hasPrefix(_:)方法計算話劇中第一幕的場景數:
    var act1SceneCount = 0
    for scene in romeoAndJuliet {
        if scene.hasPrefix("Act 1 ") {
            ++act1SceneCount
        }
    }
    print("There are \(act1SceneCount) scenes in Act 1")
    // prints "There are 5 scenes in Act 1"

參考資料:
1. https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0
2. http://wiki.jikexueyuan.com/project/swift/

2015年12月1日 星期二

iOS筆記:UIBezierPath

UIBezierPath

ex.
enter image description here

#import <UIKit/UIKit.h>

@interface RTSpaceShipView : UIView

@end
#import "RTSpaceShipView.h"

@implementation RTSpaceShipView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    bezierPath.lineWidth = 2.0;

    [bezierPath moveToPoint:CGPointMake(1/6.0 * self.bounds.size.width, 1/3.0 * self.bounds.size.height)];
    [bezierPath addLineToPoint:CGPointMake(1/6.0 * self.bounds.size.width, 2/3.0 * self.bounds.size.height)];
    [bezierPath addLineToPoint:CGPointMake(5/6.0 * self.bounds.size.width, 2/3.0 * self.bounds.size.height)];
    [bezierPath addLineToPoint:CGPointMake(2/3.0 * self.bounds.size.width, 1/2.0 * self.bounds.size.height)];
    [bezierPath addLineToPoint:CGPointMake(1/3.0 * self.bounds.size.width, 1/2.0 * self.bounds.size.height)];

    [bezierPath closePath];

    [bezierPath stroke]; //開始畫

    UIBezierPath *cockpitWindowPath = [UIBezierPath bezierPathWithRect:CGRectMake(2/3.0 * self.bounds.size.width, 1/2.0 * self.bounds.size.height, 1/6.0 * self.bounds.size.width, 1/12.0 * self.bounds.size.height)];

    [[UIColor blueColor] setFill];
    [cockpitWindowPath fill];
}


@end

2015年11月17日 星期二

iOS筆記:Creating Views Programatically

數據類型:

CGFloat: 浮點值的基本類型
CGPoint: 表示一個二維坐標系中的點
CGSize: 表示一個矩形的寬度和高度
CGRect: 表示一個矩形的位置和大小

typedef float CGFloat;// 32-bit
typedef double CGFloat;// 64-bit

struct CGPoint {
    CGFloat x;
    CGFloat y;
};
typedef struct CGPoint CGPoint;

struct CGSize {
    CGFloat width;
    CGFloat height;
};
typedef struct CGSize CGSize;

struct CGRect {
    CGPoint origin;
    CGSize size;
};
typedef struct CGRect CGRect;

Views Frame and Bounds

frame:描述當前視圖在其父視圖中的位置和大小。
bounds:描述當前視圖在其自身坐標系統中的位置和大小。
center:描述當前視圖的中心點在其父視圖中的位置。

enter image description here

ios以左上角爲坐標原點(0,0),以原點向右側爲X軸正方向,原點下側爲Y軸正方向
enter image description here

ios採用CGPoint來表示點在坐標系上X、Y位置。我們可以通過CGPointMake(x,y)來創建一個坐標點:CGPoint point = CGPointMake(80,40).

同時,ios採用CGSize來表示視圖的寬度和高度,即視圖的大小。我們可以通過CGSizeMake(width,height)來創建一個矩形的大小
CGGeometry類定義幾何元素的結構和操作幾何元素的函數。

CGRect則是結合了CGPoint和CGSize,用來表示矩形的位置和大小。它的origin表示矩形右上角所在位置(CGPoint),size表示矩形的大小(CGSize)。

frame和bounds屬性,都是用來描述視圖的大小(CGSize)和位置(CGPoint)的,兩者都用CGRect表示。不同的是,frame描述的是在其父視圖中的CGRect,而bounds描述的是在其自身視圖中的CGRect,也就是說,兩者所在的坐標系是不同的。如上圖所示,View B是View A的子視圖,那麽,View B的frame屬性爲origin(200,100),size(200,250),而View B的bounds屬性爲origin(0,0),size(200,250)。

frame和bounds是UIView中的兩個屬性(property)。

frame指的是:該view在父view坐標系統中的位置和大小。(參照點是父親的坐標系統)
bounds指的是:該view在本身坐標系統中 的位置和大小。(參照點是本身坐標系統)


Adding a UIView programmatically

ex.

CGRect myViewsFram = CGRectMake(20, 250, 200, 60);
UIView *myView = [[UIView alloc] initWithFrame:myViewsFram];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];

UIButton *anotherButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    anotherButton.frame = CGRectMake(40, 40, 200, 100);
    anotherButton.backgroundColor = [UIColor greenColor];
    [anotherButton setTitle:@"Press ME" forState:UIControlStateNormal];
    [self.view addSubview:anotherButton];

    //[myView removeFromSuperview];

Set up Target Action

- (void)viewDidLoad {
    [super viewDidLoad];
    ...
    [anotherButton addTarget:self action:@selector(didPressButton:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)didPressButton:(UIButton *)button
{
    NSLog(@"this is the test form Target/Action");
}

範例:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.mylabel = [[UILabel alloc] init];
    self.mylabel.frame = CGRectMake(20, 60, 280, 22);
    [self.view addSubview:self.mylabel];

    self.mylabel.textColor = [UIColor blueColor];
    self.mylabel.text = @"Placeholder";

    self.myTextfield = [[UITextField alloc] init];
    self.myTextfield.frame = CGRectMake(20 , 110, 280, 30);
    self.myTextfield.placeholder = @"Type text here";
    self.myTextfield.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:self.myTextfield];

    UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [myButton addTarget:self action:@selector(myButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
    [myButton setTitle:@"Press me!" forState:UIControlStateNormal];
    myButton.frame = CGRectMake(20, 160, 200, 30);
    [self.view addSubview:myButton];

}

- (void)myButtonPressed: (UIButton *)sender
{
    self.mylabel.text = self.myTextfield.text;
    [self.myTextfield resignFirstResponder];
}

2015年10月3日 星期六

iOS筆記:UITextField & UITextView &Date Picker

UITextField

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    textField1 = [[UITextField alloc] init];
    textField1.frame = CGRectMake(10, 40, 200, 150);
    textField1.backgroundColor = [UIColor lightGrayColor];
    textField1.text = @"Test";

    textField1.font = [UIFont systemFontOfSize:20];
    textField1.textColor = [UIColor purpleColor];
    [self.view addSubview: textField1];

    textField1.textAlignment = NSTextAlignmentCenter; //設定對其方式
    textField1.autocorrectionType = UITextAutocorrectionTypeNo;//是不是要訂正輸入的字

    textField1.keyboardType = UIKeyboardAppearanceDefault;
}
    testField1.Delegate = self;
//文字欄位正在進行編輯時的動作
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
    return YES;
}

//鍵盤按下 Return ( Done ) 鍵時的動作
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    //收起鍵盤
    [textField resignFirstResponder];
    return YES;
}

//TextField 文字欄位完成編輯時
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField{
    return YES;
}

#pragma UIViewDelegate
//點擊文字框以外的地方時
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    //指定欄位如在作用中,則隱藏鍵盤
    if( textField1.isTracking ){
        [textField1 resignFirstResponder];
    }
    [super touchesBegan:touches withEvent:event];
}

UITextView

使用上跟UITextField很相似

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    textView1 = [[UITextView alloc] init];
    textView1.frame = CGRectMake(40, 40, 200, 200);
    textView1.backgroundColor = [UIColor lightGrayColor];
    textView1.text = @"Default Test";

    textView1.font = [UIFont systemFontOfSize:20];
    textView1.textColor = [UIColor purpleColor];
    [self.view addSubview: textView1];

    textView1.textAlignment = NSTextAlignmentCenter; //設定對其方式
    textView1.autocorrectionType = UITextAutocorrectionTypeNo;//是不是要訂正輸入的字

    textView1.keyboardType = UIKeyboardAppearanceDefault;
    textView1.delegate = self;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    if ([text isEqualToString:@"\n"]) {
        [textView1 resignFirstResponder];
        return NO;
    }
    else {
    return YES;
    }
}

Date Picker

Apple認為選取器是實作日期跟時間選擇的最佳介面, 故提供兩種介面選擇, 一種為 Date Picker另一為自訂的Picker View.

要存取Date Picker存取的NSDate資料時就需要使用其date屬性. NSDateFormatter物件可以讓我們把NSDate物件轉換成字串. 如下面範例先初始化NSDateFormatter物件, 在使用setDateFormat方法, 建立出自訂格式. 再利用NSDateFormatter的另一個方法stringFromDate, 只要將NSDate傳入stringFromDate方法, 就會回傳指定格式.

enter image description here

測定時間差異
利用NSDate物件的timeIntervalSinceDate方法, 此方法會回傳兩個日期間的秒數差距.

- (IBAction)DateButton:(UIButton *)sender {
    NSDate *date = self.datePicker.date;

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    #設定顯示的格式
    #[formatter setDateFormat:@"yyyy-MM-dd"];
    [formatter setDateFormat:@"yyyy/MM/dd/HH:mm"];

    NSString *stringFromDate = [formatter stringFromDate:date];
    NSLog(@"%@", stringFromDate);
    NSLog(@"%@", [NSDate date]); 
    #currently date.

    int timeInterval = [date timeIntervalSince1970];  
    #Give us the time interval or haw many seconds have passed since January 1,1970.

    NSLog(@"%i", timeInterval);
}

enter image description here

參考資料
1. http://unicode.org/reports/tr35/tr35-6.html#date_format_patterns
2. http://sh932111.pixnet.net/blog/post/29625583-ios_uitextfield-%E7%9A%84%E6%87%89%E7%94%A8-(%E7%B4%94%E4%BD%BF%E7%94%A8%E7%A8%8B%E5%BC%8F%E7%A2%BC%E8%A8%AD%E5%AE%9A)
3. http://rickyy-blog.logdown.com/tags/UITextField
4. http://www.cnblogs.com/ChinaKingKong/p/4690581.html

2015年10月1日 星期四

iOS筆記:PCH file

如果需要import的程式變多了之後,所有需要的檔案都必須要寫入import有時候變成一件煩人的事情。所有專案中的程式(.h 或 .m)都會預設去import你定義在pch檔案中的所#import的header,也就是如果你有很多檔案需要#import <MapKit/MapKit.h>,你可以只寫在這個import在Prefix.pch檔案中一次,在其它程式中就不用再次宣告了。

從XCode6之後, 新建專案默認是沒有pch文件的,如果我們想使用pch文件,需要手動添加.

  1. 首先新增一個PCH File,到該目錄底下。
    enter image description here

  2. 把.pch檔放置到Supporting Files目錄下

enter image description here

  1. 設定路徑

在Prefix Header 右側連點兩下並輸入檔案的路徑, precompile 也勾YES.

enter image description here

  1. 在.pch file實作
    ex.
    enter image description here

StackOverflow神人提供的幾個步驟:
a. Make new file: ⌘cmd+N
b. iOS/Mac > Other > PCH File > YourProject-Prefix.pch.
c. Project > Build Settings > Search: “Prefix Header”.
d. Under “Apple LLVM 6.0″ you will get the Prefix Header key.
e. Type in: “YourProjectName/YourProject-Prefix.pch”.
f. Clean project: ⌘cmd+⇧shift+K
g. Build project: ⌘cmd+B

參考:
1. https://www.youtube.com/watch?v=XfBWJEj7eoY
2. https://cg2010studio.wordpress.com/2014/10/31/xcode-%E5%89%8D%E7%B7%A8%E8%AD%AF%E6%A8%99%E9%A0%AD%E6%AA%94-pre-compile-header/
3. http://www.vincenttsai.com/category/iphone%E6%87%89%E7%94%A8%E7%A8%8B%E5%BC%8F%E9%96%8B%E7%99%BC/
4. http://www.vincenttsai.com/xcode-6-%E6%B6%88%E5%A4%B1%E7%9A%84-pch/