Swift 入门学习

Swift 分享大纲

  • Swift 简介
  • Swift 优缺点
  • Swift 常用关键词的解释和用法
  • Swift 使用示例
  • Swift 与 OC 混合开发注意点
  • SwiftUI简单了解

Swift 简介

Swift 社区有详细的官网介绍和学习入门。
Swift 发布相关历史
Swift 官网快速体验
Swift 官网基础知识
Swift 是苹果于2014年 WWDC(苹果开发者大会)发布的新开发语言,是一个很强大的语言,可以用于 iOS 、iPadOS 、macOS 、tvOS 和 watchOS 等开发。目前最新版本已经到 Swift 5.5,已经趋于稳定,不会出现大量的修改的问题,而且没有了 mac 系统的开发限制,还可以使用 windows,和 Linux 系统进行开发。

Swift 是最新的编程语言研究的结果,结合了几十年构建苹果平台的经验。命名参数用干净的语法表示,这使得 Swift 中的 api 更容易阅读和维护。更好的是,您甚至不需要输入分号。推断类型使代码更整洁,更不容易出错,而模块消除头文件并提供名称空间。为了更好地支持国际语言和表情符号,Strings 是 unicode 正确的,并使用基于 UTF-8 的编码来优化各种用例的性能。使用紧凑的确定性引用计数自动管理内存,在不产生垃圾收集开销的情况下将内存使用保持在最低水平。

Swift 还有许多其他功能,可以让您的代码更富表现力:

  • 泛型强大且易用
  • 协议扩展使得泛型代码编写变得更为容易
  • 头等函数和轻量级闭包语法
  • 对范围或集合进行快速、简洁的迭代
  • 元组和多值返回
  • 支持方法、扩展和协议的结构
  • 枚举能执行有效负载并支持模式匹配
  • 函数式编程模式,例如映射和过滤
  • 使用 try / catch / throw 处理原生错误

Swift 优缺点

Swift 优点
  • swift 自动做类型推断,可以不声明类型,也保证了类型使用安全
  • swift 注重面向协议编程、函数式编程、面向对象编程,使用点语法调用
  • swift 没有运行时特性,它是一门静态语言,但是 Swift 是使用 OC 的 runtime 接口间接拥有了运行时的特性
  • swift 容易阅读,文件结构和大部分语法简易化,只有 .swift 文件,结尾不需要分号
  • swift 中的可选类型,适用于所有数据类型,而不仅仅局限于类。相比于 OC 中的 nil 更加安全和简明
  • swift 中的[泛型类型]更加方便和通用,而非 OC 中只能为集合类型添加泛型
  • swift 中各种方便快捷的[高阶函数](函数式编程)( Swift 的标准数组支持三个高阶函数: mapfilterreduce ,以及 map 的扩展 flatMap
  • swift 新增了两种权限,细化权限。open > public > internal(默认)> fileprivate > private
  • swift 中独有的元组类型( tuples ),把多个值组合成复合值。元组内的值可以是任何类型,并不要求是* 相同类型的。
  • swift 支持函数式编程,OC 本身是不支持的,需要通过引入 ReactiveCocoa 这个库才可支持函数式编程。

    函数式编程

    1. 代码简洁,开发快速
      函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。
    2. 接近自然语言,易于理解
      函数式编程的自由度很高,可以写出很接近自然语言的代码。
      表达式(1 + 2) * 3 - 4,写成函数式语言:
      subtract(multiply(add(1,2), 3), 4)
      对它进行变形,不难得到另一种写法:
      add(1,2).multiply(3).subtract(4)
      因此,函数式编程的代码更容易理解。
    3. 更方便的代码管理
      函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。
    4. 易于"并发编程"
      函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。
Swift 缺点
  • Swift 与 OC 混编开发项目时,OC 不能继承 Swift 类。

  • Swift 与 OC 混编项目打包 framework 需要注意(纯 Swift 项目打包 framework 还未进行过,暂时不知),如果需要真机和模拟器的合并包,那就需要修改 XXXXX-swift.h 文件。修改模拟器中或者真机其中的任意一个文件即可,修改之后的文件放入合并包中。

    // 模拟器
    #if 0
    #elif defined(__x86_64__) && __x86_64__
    
    // 真机
    #if 0
    #elif defined(__arm64__) && __arm64__
    

    修改这两行代码为

    #if defined(__x86_64__) && __x86_64__ || (__arm64__) && __arm64__
    
  • App体积变大:使用 Swift 后, App 体积大概增加 5-8 M 左右,对体积大小敏感的慎用。(体积变大的原因是因为 Swift 还在变化,所以 Apple 没有在 iOS 系统里放入 Swift 的运行库,反而是每个 App 里都要包含其对应的 Swift 运行库。)

  • 上线方式改变:不能使用application Loader上传包文件,会提示你丢失了swift support files,只能使用xcode直接上传。 [这条有待测试,网上的资料]

Swift 常用的关键词解释和用法

详细的 Swift 关键词解释和用法比较多,就不详细讲解了,就列举几个开发中一定要用的或者最常使用的。
基础的学习可以使用 Swift 的 Playground ,可以不用运行 cmd + r 就可以看到结果,体验不错。

  • 权限类型
    首先了解一个模块是什么?可以这样理解:一个APP就是一个模块,一个第三方API,第三放框架等都是一个完整的模块。
    public 权限
    public:共有访问权限,类或者类的公有属性或者公有方法可以从文件或者模块的任何地方进行访问。如果要对该模块外留有访问的属性或者方法,就应该使用 public 的访问权限,public 的权限无法在其他模块被复写方法、属性或被继承。
    open 权限
    open:公开权限;最高的权限,可以被其他模块访问,继承及复写。只能用于类和类的成员。
    internal 权限(默认)
    internal:internal 是内部的意思,即有着 internal 访问权限的属性和方法说明在模块内部可以访问,超出模块内部就不可被访问了。在 Swift 中默认就是 internal 的访问权限
    private 权限
    private:私有访问权限,被 private 修饰的类或者类的属性或方法可以在同一个物理文件中的同一个类型(包含 extension )访问。如果超出该物理文件或不属于同一类型,那么有着 private 访问权限的属性和方法就不能被访问。
    fileprivate 权限
    fileprivate:文件私有访问权限,被 fileprivate 修饰的类或者类的属性或方法可以在同一个物理文件中访问。如果超出该物理文件,那么有着 fileprivate 访问权限的类,属性和方法就不能被访问。

    权限从高到低排序如下:
    open > public > internal > fileprivate > private

    权限使用潜规则
    1、如果一个类的访问级别是 fileprivateprivate 那么该类的所有成员变量都是 fileprivateprivate (此时成员无法修改访问级别),如果一个类的访问级别是 openinternal 或者 public 那么它的所有成员都是 internal,类成员的访问级别不能高于类的访问级别(注意:嵌套类型的访问级别也符合此条规则)
    2、常量、变量、属性、下标脚本访问级别低于其所声明的类型级别,并且如果不是默认访问级别( internal )要明确声明访问级别(例如一个常量时一个 private 类型的类类型,那么此常量必须声明为 privatefileprivate
    3、在不违反1、2两条潜规则的情况下,setter 的访问级别可以低于 getter 的访问级别(例如一个属性访问级别是 internal,那么可以添加 private(set) 修饰符将 setter 权限设置为 private,在当前模块中只有此源文件可以访问,对外部是只读的)。
    4、必要构造方法( required 修饰)的访问级别必须和类访问级别相同,结构体的默认逐一构造函数的访问级别不高于其他成员的访问级别(例如一个成员是 private 那么这个构造函数就是 private,但是可以通过自定义来声明一个 public 的构造函数),其他方法(包括其他构造方法和普通方法)的访问基本遵循潜规则1

    什么时候需要使用 required
    Swift 不会自动继承父类的构造器方法,但是在这两个条件下会继承
    如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
    如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。
    所以父类里有 init 方法,子类里并不一定会有,那么当你要求子类必须有父类的init方法时,需在父类 init 方法前加上 required

  • 类和结构体
    struct: 结构体

    • 值类型(类似于深 copy
    • 没有继承,也没有重写、多态等特性
    • 编译后,方法地址就是固定明确的,所以方法的运行效率高
    • 栈内存(StringArrayDictionary 等发生 copy 时,会存到堆中)
    • 结构体中的 StringArrayDictionary 也都是结构体实现
    • 自动生成初始化器(如下图1)
    • 必须保证所有的成员都有值

    如果某个成员是可为 nil 的,且是 var 类型则初始化时不必赋值
    类和结构体的成员变量如果都是 var 类型且是可为空的则不必所有成员变量都初始化

    class: 结构体

    • 引用类型(类似于浅 copy
    • 有继承,有重写、多态等特性
    • 汇编代码比结构体多且复杂,因为有重写、多态等特性,不能固定地址调用,运行效率低。
    • 堆内存
    • 类中的 NSStringNSArrayNSDictionary 也都是类实现
    • 不会自动生成初始化器,需要手动编写初始化器(如图1)
    图1 结构体自动生成初始化器,而类不会生成初始化器

    值类型的赋值是深拷贝。
    在 swift 标准库中为了提升性能,对于系统中的 StringArrayDictionary 采取了Copy On Write 技术,只有当被拷贝的数据发生变化时才进行深拷贝(联想 OC 中的 copy )。
    如果初始化后需要改变成员变量的值,class 不必声明成可变类型,而 struct 必须声明称可变类型。

  • 常量与变量
    let 声明常量,使用中不能再次改变值,再次改变会编译报错。
    var 声明变量,可以多次赋值或修改。
    Swift 是语言安全型的,会对没有注释类型的常量或变量进行推断出类型。

    // 声明常量
    let letMessage = "Hello"  // 推断是 String 类型
    let letMessage: String = "Hello" 
    letMessage = "Message" // 直接报错,error,因为是常量,会提示将 let 改为 var
    // 声明变量
    var varMessage = "Hello"  // 推断是 String 类型
    var varMessage: String = "Hello"
    varMessage = "Message" // 正常
    
  • 函数 func

    func greet(person: String, alreadyGreeted: Bool) -> String {
        if alreadyGreeted {
            return greetAgain(person: person)
        } else {
            return greet(person: person)
        }
    }
    
    func stepForward(_ input: Int) -> Int {
        return input + 1
    }
    func stepBackward(_ input: Int) -> Int {
        return input - 1
    }
    // 以  函数 (Int) -> Int  作为返回值
    func chooseStepFunction(backward: Bool) -> (Int) -> Int {
        return backward ? stepBackward : stepForward
    }
    
  • 开发可以提供 OC 使用的属性或方法
    需要在属性或方法前面加 @objc

    @objc public var currentIndex: Int?
    @objc public func showSingle(name: String? = "默认") {}
    
  • 安全判断 guard ... else { ... return } 也可以使用 if let

    guard arr.count > 0  else {  return }
    // 只有 arr.count > 0 为真才能往下走
    var name:String?
    if let nameS = name {
    // nameS 不等于 nil 才会执行
    }
    
  • 扩展 [类似于 OC 中的分类] extension
    extension:允许给已有的类、结构体、枚举、协议类型,添加新功能。

    class Person  
    {  
        var name:String = ""  
        var age:Int = 0  
        var gender:String = ""  
    }
    
    extension Person  {  
        func printInfo()  {  
            print("My name is \(name), I'm \(age) years old and I'm a \(gender).")  
        }  
    }
    
  • 类型转换
    as:类型转换运算符,用于尝试将值转成其它类型。

    let age = 28 as Int
    let money = 20 as CGFloat
    let cost = (50 / 2) as Double
    
    switch person1 { 
    case let person1 as Student: 
        print("是Student类型,打印学生成绩单...") 
    case let person1 as Teacher: 
        print("是Teacher类型,打印老师工资单...") 
    default: break
    }
    
  • 调试台打印 print

    print("123456")
    //  123456
    let number = "666"
    print(number)
    // 666
    print("num==\(number)")
    // num==666
    

Swift 开发中常用示例

1、基础属性 letvar

let name = "Baron"
var age = 26
var current: String? // 可选型,可以为nil
var currentNormal: String? = "normal"  // 可选型,默认为 "normal"
@objc  var arrayString: [String]?  // 数组,字符串类型 可以在 OC 中使用
private var dicString: [String:String]? // 字典,字符串类型 私有不能跨文件使用

2、函数 func

// funcname 方法名称; name:调用时显示的参数名称; str 函数内使用的参数名称;returntype 返回值
func funcname(name str:String) -> returntype
{
  //  code...
   return parameters
}
// 无参数 无返回值 是类方法
class func funcname() 
{
   Statement1
}

// 无参数 无返回值 是 OC 方法 一般按钮绑定的的方法要使用 @objc
@objc func funcname() 
{
   Statement1
}

3、协议 protocol,与 OC 有不同之处,可以有默认实现。

//oc协议默认可选 swift默认必须实现
@objc protocol VCTypeProtocol where Self: UIViewController  {
    var model: Model { get set }
    func shouldShow(container: VC, completed:@escaping (Bool) -> Void)  // @escaping (Bool) -> Void 是逃逸闭包, 类似 OC 中的异步 block
    func test()
}

extension VCTypeProtocol {
      func test() {
       // 默认实现,继承 VCTypeProtocol 可以不用实现 test 函数就可以直接调用。
      }
}

4、闭包 类似于 OC Block,但是逃逸闭包一般会放在函数的后面实现。

// 无参数 无返回值 
() -> ()
// name:参数 ; String 类型返回值 
(String) -> String

// 逃逸闭包, 类似 OC 中的异步 block,网络回调基本都是逃逸闭包
@escaping (Bool) -> Void)

5、条件判断 if 语句if...else 语句, if...else if...else 语句, switch 语句, guard ... else { ... return }

let name:String? = "lisa"
if  let nameS = name  {
    // nameS 不是Nil 执行
}
else {
     // nameS 是Nil 执行
}
switch expression {
   case expression1  :
      statement(s)
      fallthrough /* 不写这句等于是 break,写了就会执行下面的 case */
   case expression2, expression3  :
      statement(s)  // 默认是 break 的情况  
   default : /* 如果上面的情况已经全部满足可以不用写 defult */
      statement(s);
}
guard let nameS = name else {
     // nameS 是Nil 执行
     return
}
// nameS 不是Nil 执行

6、集合类型
数组(Array): 使用有序列表存储同一类型的多个值, 相同的值可以出现在不同位置中.
集合(Set): 存储相同类型并且没有确定顺序的值, 每个元素只出现一次.
字典(Dictionary): 使用无序的键值对存储相同类型的键和相同类型的值, 每个值(value)都关联唯一的键(key).

字面量创建方式 Array [] , Dictionary [:],Set 有点类似 Array []

// 声明的类型可以不写,Swift 会自动推断, 写声明是为了我们开发者自己可以清楚地知道是什么类型
var dic:Dictionary<String, String> = ["1":"1","2":"2"]
// Dictionary 可以用 int 做 key
var dic2:Dictionary<Int, String> = [1:"1",2:"2"]
var strSet:Set<String> = ["1","2"]
var strArr:Array<String> = ["1","2"]

strArr += ["3","4"]  
// strArr ["1","2","3","4"]
strArr.append("Flour")
// strArr ["1","2","3","4","Flour"]

7、循环遍历,控制流

for item in strArr {
    print("Item:\(item)") 
}
// Item:1
// Item:2
// Item:3
// Item:4
// Item:Flour

for(index,item) in strArr.enumerated() {
    print("index:\(index); Item:\(item)") 
}
// index:0; Item:1
// index:1; Item:2
// index:2; Item:3
// index:3; Item:4
// index:4; Item:Flour

for(key,value) in  dic {
    print("key:\(key); value:\(value)") 
}
// key:1; value:1
// key:1; value:2

// Dictionary 可以用 int 做 key
var dic:Dictionary<Int, String> = [1:"1",2:"2"]
for(key,value) in  dic {
    print("key:\(key); value:\(value)")
}
// key:1; value:1
// key:1; value:2

8、可选型
可选型是在后面添加 ? 来表示的,表示有可能为 nil。在使用是,要进行安全判断,如果确定有值可以进行强制解析,! 表示强制解析,强制解析之后如果是空值,就会崩溃,! 强制解析不安全,尽量的不使用。可以用 ?? 来避免,也可以 if letguard let ... else { return }
?? a ?? b 表示 a 如何不存在即使用 b

var name:String?
// 省略代码 ...
// 后面使用 name,不能确定是否有值,
// 1、可以使用 `??`
let nameS = name ?? "lisa" //  lisa 表示默认值, 也可以默认 "" 空字符串
// 2、if let
if let nameS = name {
// nameS 有值时或是空字符串"" ,才会执行
}
// 3、guard let ... else { return }
guard let nameS = name else {   
    // nameS 为 nil 时,会执行这里面 
    return 
}
// nameS 有值时或是空字符串"",才能执行下面

9、泛型
说到泛型,会想到 Swift 中的 AnyAnyObject 和 OC 中的 id 。那么都有什么区别呢

OC 中的 id

  • id 的定义中,已经包好了*号。id 指针只能指向OC中的对象
  • 为了尽可能的减少编程中出错,Xcode 做了一个检查,当使用 id 类型的调用本项目中所有类中都没有的方法,编译器会报错
  • id 类型不能使用 . 语法,因为 . 语法是编译时特性,而 id 是运行时特性

Swift 中的 AnyAnyObject

概括来说 AnyObject 用于任何类(class)的实例,而 Any 可以用于表示任何变量,包括各种基本类型、值类型以及实例。而在 swift 中,枚举类型和结构体(例如 ArrayDictionary )都属于值类型,因而不能用 AnyObject 来进行修饰。

Swift 中的泛型

通常来讲,泛型为类或者方法提供一个类型参数,以方便某个参数类型保持前后的一致性。

Swift 中的泛型和 Any

//泛型修饰
 func singleGenericFunc<T>(x: T ,y: Int)-> T {
    ......
}

//Any修饰
func singleAnyFunc(x: Any,y:Int) -> Any {
  ......
}

此处最大的不同是,singleGenericFunc 中的参数 x 的类型与方法的返回类型是一致的。而singleAnyFunc 中却没有这个特性。x 与方法的返回类型都可以是任意值,不一定相同。
这是由于泛型的类型检查由编译器负责,而 Any 修饰则避开了类型系统。
综合比较而言,应该尽量多使用泛型,少使用 Any ,以尽量转换类型时发生的类型错误。

10、常见错误处理
常用的有 do-catchtry?、或 try!

Swift 中的错误处理类似于其他语言中的异常处理,使用 try , catchthrow 关键字。与许多语言中的异常处理(包括 Objective-C)不同,Swift 中的错误处理不涉及展开调用堆栈,这是一个计算成本很高的过程。因此,throw 语句的性能特征与语句的性能特征相当 return

要指示函数、方法或初始值设定项可以抛出错误,请 throws 在函数声明中的参数后面写上关键字。标记 throws 为 的函数称为抛出函数。如果函数指定了返回类型,则 throws 在返回箭头 ( ->)之前写上关键字。

func canThrowErrors() throws -> String
func cannotThrowErrors() -> String

您可以使用 do-catch 语句通过运行代码块来处理错误。如果 do 子句中的代码抛出错误,则将其与 catch 子句进行匹配以确定其中哪一个可以处理错误。

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
} catch pattern 3, pattern 4 where condition {
    statements
} catch {
    statements
}

提倡前往Swift 官网学习

Swift 与 OC 混合开发

1、创建并添加桥接文件 (桥接文件是 Swift 使用 OC 是用的)
如果是 OC 项目与 Swift 混编,创建第一个 Swift 文件,系统会自动生成桥接文件并配置。
如果是 Swift 项目与 OC 混编,创建第一个 OC 文件,系统会自动生成桥接文件并配置。
如果删除了,就需要手动创建 projectName-Bridging-Header.h 的桥接文件,并需要自己手动配置。在 Targets->Bulid Setting ,然后搜索 Swift,选择 Swift Compiler-General 下 Objective-C Bridging Header ,然后配置文件路径。
2、使用方法
Swift 文件使用 OC, 只需要把 OC 的 .h 文件导入在 projectName-Bridging-Header.h 文件中即可

Swift 文件使用 OC 文件

OC 文件使用 Swift ,需要在 OC 文件中导入头文件 projectName-Swift.h
projectName-Swift.h(这个是 xcode 自动生成的,并配置好的路径,是找不到文件,但是真是生成的,是把 Swift 编译成 OC 的形式)

OC 文件使用 Swift 文件

3、OC宏文件
Swift 中是不能使用宏定义语法,但是因为命名空间的缘故,在其中,我们将原本 OC 中不需要接受参数的宏,定义成 let 常量或枚举,将需要接受参数的宏定义成函数。

let UScreenWidth = UIScreen.main.bounds.width
let UScreenHeight = UIScreen.main.bounds.height

var topVC: UIViewController? {
    var resultVC: UIViewController?
    resultVC = _topVC(UIApplication.shared.keyWindow?.rootViewController)
    while resultVC?.presentedViewController != nil {
        resultVC = _topVC(resultVC?.presentedViewController)
    }
    return resultVC
}

var isIphoneX: Bool {
    return UI_USER_INTERFACE_IDIOM() == .phone
        && (max(UIScreen.main.bounds.height, UIScreen.main.bounds.width) == 812
        || max(UIScreen.main.bounds.height, UIScreen.main.bounds.width) == 896)
}

private  func _topVC(_ vc: UIViewController?) -> UIViewController? {
    if vc is UINavigationController {
        return _topVC((vc as? UINavigationController)?.topViewController)
    } else if vc is UITabBarController {
        return _topVC((vc as? UITabBarController)?.selectedViewController)
    } else {
        return vc
    }
}

5、开发时提示情况
Swift 文件中使用 OC 的方法或属性时,有可能没有提示,需要敲代码出来,如果敲的正确,可以正常编译和运行。

SwiftUI

SwiftUI官网
SwiftUI简单认识,SwiftUI 是一种使用Swift语言在苹果设备上构建用户界面的创新且简单的方式。使用 SwiftUI 在苹果设备上创建用户界面可以使用一套统一的工具和 API。SwiftUI 使用声明式的 Swift 语法,代码易读并且写起来很自然。同时它可以和 Xcode 中的设计工具配合使用,让设计工具中的展示样式和代码同步起来。使用 SwiftUI 创建的用户界面自动支持了动态类型(Dynamic Type)、暗黑模式(Dark Mode)、语言本地化(Localization)以及所有人都可以使用的可访问性(Accessibility),也就是说,创建用户界面时,SwiftUI 写的代码比使用其它方式写的代码具有更多的功能。

SwiftUI简单界面搭建

这种声明式语法与最近很火的快平台开发语言 Flutter 一样。SwiftUI 就不细讲了,总体来说比较简单,重点在于大量的实践,只有大量实践才能对 API 快速掌握和熟练应用。
那么为什么会提到 SwiftUI 呢,那就要先了解一下 iOS 提出的小组件功能了。

ios 常用小组件开发

1、Today Widget 小组件
Today Widget 小组件
2、WidgetExtension 小组件
WidgetExtension 小组件

自iOS8之后,苹果支持了扩展(Extension)的开发,开发者可以通过系统提供给我们的扩展接入点 (Extension point) 来为系统特定的服务提供某些附加的功能。
但iOS14后,苹果更新了扩展组件,引入了新的UI组件:WidgetKit 而舍弃了iOS14以下版本的 Today Extension 组件

WidgetExtension 使用的是新的 WidgetKit 不同于 Today Widget,它只能使用 SwiftUI 进行开发,所以需要 SwiftUI 和 Swift 基础
Widget 只支持3种尺寸systemSmall(2x2)、systemMedium(4x2)、systemLarge(4x4)
默认点击 Widget 打开主应用程序
Widget 类似于 Today Widget 是一个独立运行的程序,需要在项目中进行 App Groups 的设置才能使其与主程序互通数据,这点与 Today Widget 相同
Apple官方已经弃用 Today Extension,Xcode12 已经不再提供 Today Extension 的添加,已经有 Today Widget 的应用则会显示到一个特定的区域进行展示

所以如果需要开发 WidgetExtension 小组件就需要了解 SwiftUI 了。

提供学习的 Swift 项目

仿有妖气漫画项目:https://github.com/spicyShrimp/U17

提供学习的 SwiftUI 项目

SwiftUI 官网教程:https://swiftui.jokerhub.cn/tutorials
Copy官网学习项目:https://gitee.com/sihj/swift-ui_study

参考博客

Swift 社区
Swift 官网快速体验
Swift 官网基础知识
Swift 介绍及优缺点
Swift 中的权限控制
Swift 关键词解释和用法
Swift对比Objective-C的优缺点

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容