Swift开发学习

创建一个新的APP

在Xode的欢迎界面中,点击 Create a new Xcode project 创建新项目。模板选择器可以帮助你利用预设模板创建新项目,在 macOS 选项卡中你可以看到四种不同的核心macOS app类型:

  • App: 基于窗口UI的macOS桌面应用,Cocoa是所有macOS应用程序的框架。
  • Document App:
  • Game: 基于苹果SpriteKit或SceneKit框架的游戏程序。
  • Command Line Tool: 基于命令行文本UI,在shell中运行的实用工具。

选择 App 并点击 Next,在下一界面中设置产品名称为 HelloWorld,其他保持默认。

最后再点击 Next,并选择你要保存项目的位置。

Swift 的 playground 就像是一个可交互的文档,它是用来练手学swift的,写一句代码出一行结果(右侧),可以实时查看代码结果,是学习swift语言的利器!

Swift语言

基本类型

使用 let 创建一个常量,var 创建一个变量。

1
2
3
var myVar = 100
myVar = 50
let PI = 3.1415926

每个常量和变量在Swift中都有一个类型,但是不需要显式地把类型写出来。当你声明一个常量或变量的时候,提供给这个常量或变量一个值,让编译器去推断它的类型。

如果初始值提供不了足够的信息(或没有初始值),那么就需要在变量名之后为其指定类型,中间通过冒号分隔。

1
2
3
let implicitIntrger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70

如果你需要进行类型转换,就显式声明:

1
2
3
let label = "The width is "
let width = 94
let widthLabel = label + String(width)

还有一种更简单的方法:

1
2
3
4
let apple = 3
let pear = 5
let appleSum = "I have \(apple) apples."
let fruitSum = "I have \(apple + pear) pieces of fruit."

使用三引号(“””)表示占用多行字符串:

1
2
3
4
let quotation = """
I said "I have \(apple) apples."
And then I said "I have \(apple + pear) pieces of fruit."
"""

使用 [] 创建数组和字典:

1
2
3
4
5
6
7
8
9
10
11
var list = ["A", "B", "C"] //字符也要用"",因为是作为String类型的
list[1] = "P"
// then list is ["A", "P", "C"]
// index starts from 0

var dict = [
"Malcolm": "Captain"
"Kaylee": "Mechanic"
]
dict["preccrep"] = "programmer"
print(dict)

向数组中添加元素时,数组自动增长:

1
2
list.append("D")
print(list)

创建空数组或空字典:

1
2
let empArr: [String] = []
let empDict: [String: Float] = [:]

如果数据类型可以被推断,只需简单地写成:

1
2
empArr = []
empDict = [:]

条件控制

使用 ifswitch 来创建条件语句,使用 for-in, whilerepeat-while 创建循环。条件加不加括号都可以,但是后面的内容必须用花括号括起来。

1
2
3
4
5
6
7
8
9
10
let scores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in scores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)

这里注意,条件必须是显式的布尔表达式,像C/C++里的 if(score) 是行不通的,而是要写为 if score != 0

在if语句中使用可选绑定来检查可选类型是否有值:

1
2
3
4
5
6
7
8
9
10
11
12
13
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
// print: Hello, John Appleseed

var optionalName: String? = nil
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
// print: Hello!

实际上,当optionalName = nil时,if的条件为false。其实很好理解,就是先让name等于optionalName,然后判断name是不是nil。An optional value either contains a value or contains nil to indicate that a value is missing. Write a question mark (?) after the type of a value to mark the value as optional.

Another way to handle optional values is to provide a default value using the ?? operator. If the optional value is missing, the default value is used instead.

1
2
3
let nickname: String? = nil
let fullname: String = "John Appleseed"
let informalGreeting = "Hi \(nickname ?? fullname)"

Switches support any kind of data and a wide variety of comparison operations—they aren’t limited to integers and tests for equality.

1
2
3
4
5
6
7
8
9
10
11
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}

After executing the code inside the switch case that matched, the program exits from the switch statement. Execution doesn’t continue to the next case, so you don’t need to explicitly break out of the switch at the end of each case’s code.

You use for-in to iterate over items in a dictionary by providing a pair of names to use for each key-value pair. Dictionaries are an unordered collection, so their keys and values are iterated over in an arbitrary order.

1
2
3
4
5
6
7
8
9
10
11
12
13
let interestingNumbers = [
"Prime": [2,3,5,7,11,13],
"Fibonacci": [1,1,2,3,5,8],
"Square": [1,4,9,16,25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for numebr in numbers {
if number > largest {
largest = number
}
}
}

Use while to repeat a block of code until a condition changes. The condition of a loop can be at the end instead, ensuring that the loop is run at least once.

1
2
3
4
5
6
7
8
9
10
11
var n = 2
while n < 100 {
n *= 2
}
print(n)

var m = 2
repeat {
m *= 2
} while m < 100
print(m)

You can keep an index in a loop by using ..< to make a range of indexes.

1
2
3
4
5
var total = 0
for i in 1..<4 {
total += i
}
print(total) // 6

Use ..< to make a range that omits its upper value, and use ... to make a range that includes both values.

1
2
3
4
5
var total = 0
for i in 1...4 {
total += i
}
print(total) // 10

函数和方法

函数声明可以包含0个或者多个参数,格式如name:Type。这些参数是额外的信息,当你去调用方法的时候,你必须把这些额外的信息传递进去。另外,函数有可选的返回类型,格式如:->返回类型,这表明这个函数返回的结果。函数的视线写在大括号({})中。

1
2
3
4
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

By default, functions use their parameter names as labels for their arguments. Write a custom argument label before the parameter name, or write _ to use no argument label.

1
2
3
4
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

Use a tuple to make a compound value—for example, to return multiple values from a function. The elements of a tuple can be referred to either by name or by number.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func calc(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0

for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calc(scores: [5,3,100,3,9])
print(statistics.sum) // 120
print(statistics.2) // 120

Functions can be nested. Nested functions have access to variables that were declared in the outer function. You can use nested functions to organize the code in a function that’s long or complex.

1
2
3
4
5
6
7
8
9
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()

Functions are a first-class type. This means that a function can return another function as its value.

1
2
3
4
5
6
7
8
func makeIncrementer -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)

A function can take another function as one of its arguments.

1
2
3
4
5
6
7
8
9
10
11
12
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
hasAnyMatches(list: [20,19,7,12], condition: lessThanTen)

Functions are actually a special case of closures: blocks of code that can be called later. The code in a closure has access to things like variables and functions that were available in the scope where the closure was created, even if the closure is in a different scope when it’s executed—you saw an example of this already with nested functions. You can write a closure without a name by surrounding code with braces ({}). Use in to separate the arguments and return type from the body.

1
2
3
4
5
6
var numbers = [20, 19, 7, 12]
print(numbers.map({ (number: Int) -> Int in
let res = number * 3
return res
}))
// [60, 57, 21, 36]
map: Returns an array containing the results of mapping the given closure over the sequence’s elements.
Declarationfunc map<T>(_ transform: (Int) throws -> T) rethrows -> [T]
DiscussionIn this example, map is used first to convert the names in the array to lowercase strings and then to count their characters.let cast = ["Vivien", "Marlon", "Kim", "Karl"] let lowercaseNames = cast.map { $0.lowercased() } // 'lowercaseNames' == ["vivien", "marlon", "kim", "karl"] let letterCounts = cast.map { $0.count } // 'letterCounts' == [6, 6, 3, 4]
ParameterstransformA mapping closure. transform accepts an element of this sequence as its parameter and returns a transformed value of the same or of a different type.-No description.
ReturnsAn array containing the transformed elements of this sequence.

You have several options for writing closures more concisely. When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both. Single statement closures implicitly return the value of their only statement.

1
2
3
let mappedNumbers = numbers.map({ number in 3*number })
print(mappedNumbers)
// [60, 57, 21, 36]

You can refer to parameters by number instead of by name—this approach is especially useful in very short closures. A closure passed as the last argument to a function can appear immediately after the parentheses. When a closure is the only argument to a function, you can omit the parentheses entirely.

1
2
let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers) // [20, 19, 12, 7]

对象与类

Use class followed by the class’s name to create a class. A property declaration in a class is written the same way as a constant or variable declaration, except that it’s in the context of a class. Likewise, method and function declarations are written the same way.

1
2
3
4
5
6
7
8
9
10
class Shape {
var numberOfSides = 0
let constSide = 10
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
func returnSides() -> Int {
return constSide + 10
}
}

Create an instance of a class by putting parentheses after the class name. Use dot syntax to access the properties and methods of the instance.

1
2
3
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

Use init:

1
2
3
4
5
6
7
8
9
10
11
12
class NamedShape {
var numberOfSides = 0
var name: String

init(name: String) {
self.name = name
}

func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

Override:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Square: NamedShape {
var sideLength: Double

init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}

func area() -> Double {
return sideLength * sideLength
}

override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Circle: NamedShape {
var radius: Double
let PI = 3.1415926

init(radius: Double, name: String) {
self.radius = radius
super.init(name: name)
}

func getArea() -> Double {
return PI * radius * radius
}

override func simpleDescription() -> String {
return "This is a circle with name \(name), radius \(radius) and area \(getArea())."
}
}
var c1 = Circle(radius: 1.23, name: "MyCircle")
print(c1.simpleDescription())

getter and a setter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
// Prints "9.3"
triangle.perimeter = 9.9
print(triangle.sideLength)
// Prints "3.3000000000000003"

If you don’t need to compute the property but still need to provide code that’s run before and after setting a new value, use willSet and didSet. The code you provide is run any time the value changes outside of an initializer. For example, the class below ensures that the side length of its triangle is always the same as the side length of its square.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)

Optional value:

1
2
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

枚举和结构体

Use enum to create an enumeration. Like classes and all other named types, enumerations can have methods associated with them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Rank: Int {
case ace = 10
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue // 10

By default, Swift assigns the raw values starting at zero and incrementing by one each time, but you can change this behavior by explicitly specifying values. In the example above, ace is explicitly given a raw value of 1, and the rest of the raw values are assigned in order. You can also use strings or floating-point numbers as the raw type of an enumeration. Use the rawValue property to access the raw value of an enumeration case.

Use the init?(rawValue:) initializer to make an instance of an enumeration from a raw value. It returns either the enumeration case matching the raw value or nil if there’s no matching Rank.

1
2
3
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
Summary: A half-open interval from a lower bound up to, but not including, an upper bound.
Declarationstruct Range<Bound> where Bound : Comparable
DiscussionYou create a Range instance by using the half-open range operator (..<).let underFive = 0.0..<5.0You can use a Range instance to quickly check if a value is contained in a particular range of values. For example:underFive.contains(3.14) // true underFive.contains(6.28) // false underFive.contains(5.0) // false``Range instances can represent an empty interval, unlike ClosedRange.let empty = 0.0..<0.0 empty.contains(0.0) // false empty.isEmpty // trueUsing a Range as a Collection of Consecutive ValuesWhen a range uses integers as its lower and upper bounds, or any other type that conforms to the Strideable protocol with an integer stride, you can use that range in a for-in loop or with any sequence or collection method. The elements of the range are the consecutive values from its lower bound up to, but not including, its upper bound.for n in 3..<5 { print(n) } // Prints "3" // Prints "4"Because floating-point types such as Float and Double are their own Stride types, they cannot be used as the bounds of a countable range. If you need to iterate over consecutive floating-point values, see the stride(from:to:by:) function.

Most app UIs are very visual experiences, so most accessibility work focuses on VoiceOver — a screen reader that lets people with low to no vision use Apple devices without needing to see their screens. VoiceOver reads out information to users about your app’s UI elements. It’s up to you to make sure this information helps users interact efficiently with your app.

协议和扩展

使用 protocol 声明一个协议:

1
2
3
4
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}

类、枚举和结构都可以采用协议:

1
2
3
4
5
6
7
8
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}

Author

preccrep

Posted on

2021-07-05

Updated on

2021-07-23

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.