美文网首页iOS Developer
iOS Apprentice中文版-从0开始学iOS开发-第二十

iOS Apprentice中文版-从0开始学iOS开发-第二十

作者: Billionfan | 来源:发表于2017-08-06 23:48 被阅读312次

    我的位置APP(MyLocations App)

    在本次课程中将介绍来自iOS SDK中的更加诱人的技术,并且你将掌握更加高级的Swift技能。

    你将要制作一个“我的位置”app,它将使用Core Location框架来获取GPS坐标来定位用户的位置,你还要将用户常去的地点用Map Kit展示出来,还可以使用户通过iPhone的摄像头拍摄上传照片,或者从照片库里获取照片上传到app,最后,你要用Core Data将所有东西存储到数据库中。听起来工作好多啊。

    app完成后将会是下面这个样子:

    我的位置app

    我的位置app可以让你将你觉得有趣的风景拍照,保存下来,以列表的形式展现出来。当你带着iPhone去旅游的时候,你可以通过点击“Get My Location(获取我的位置)”按钮来启动GPS功能,并且得到相应的街道名称。然后你可以再拍一张照片,并且添加一些描述,将这条信息保存到我的收藏中,以便将来拿出来回忆。把这个app想象为一个旅游相册吧!

    和往常一样,为了能正确的掌握工作量,你要讲这个大工程分解为许多小块:

    1、首先你要指出如何通过Core Location框架获取GPS坐标,以及如何将坐标转换为街道信息,这就叫做坐标转换(reverse geocoding)。使用Core Location可以轻易实现这一点,但是对于那些无法直接转换的地址,我们就要用点手段了。

    2、一旦你成功获取了坐标,你就要创建一个Tag Location(标注位置)界面,让用户输入一些关于这个地点的描述。这个界面是一个table view controller,并且将使用静态cell,和上一个课程的内容非常相似。

    3、然后你要将这个位置信息存储到Core Date中。之前的课程你是将数据保存到了plist文件中,对于简单的小型app是没问题的,但是对于比较复杂的app,专业的手段就是使用Core Data。它并没有听上去那么可怕,不用担心。

    4、紧接着,你要使用Map Kit框架将这个位置展示在地图上。

    5、Tag Location界面上应该有一个Add Photo(新增照片)的按钮,你可以通过这个按钮连接到iPhone的摄像头或者照片库,进行拍照或者选择照片。

    6、最后,给整个app来个大整容。同时添加一些音效和动画效果来充实app。

    在你得到上述功能前,我们要先讲一些理论知识。你需要先熟悉一下Swift语言和面向对象编程概念。

    复习一下之前遇到的Swift语法

    在之前的课程中,我已经给你展示了一些Swift的语法,但是那只是Swift的一小部分。之前我或多或少的对那些代码进行了一点讲解和标注,是你不会完全陷入迷茫。

    这样做对之前的课程是没问题的,通过一些简单的讲解可以使你勉强跟上思路,但是现在,我们要仔细的讲一下这些内容了。

    首先,我们来复习一下目前遇到过的东西。

    变量、常量和类型

    一个变量是一个指定类型的值的临时容器:

    var count: Int
    var shouldRemind: Bool
    var text: String
    var list: [ChecklistItem]
    

    数据类型,或者简称为类型,用来决定变量可以存储那种类型的值。有些变量仅存储一些简单的类型,比如Int(整型)、Bool(布尔型),而有些则存储复杂一些的,比如String(字符串)、Array(数组)。

    到目前为止,你用过的基础类型为:Int,用于存放整数;Float,用于存放小数,以及Bool,用于存放逻辑值(true和false)。

    还有一些基础类型是目前为止你没有见过的:

    Double,和Float类似,只是精度更高。在本次课程中你讲会使用Double来存储一个地理位置的经度和纬度。

    Character,仅仅存放一个字符。可以把String当作是Character的集合。

    UInt,Int的一个变形,你偶尔会遇到。U是代表“unsigned(无符号的)”,这种类型只能存储正整数。它被称作是无符号的就是因为不能在数组前放置(-)负号。UInt的存储范围是0到18万亿,不包含负数。

    Int8,Uint8,Int16,UInt16,Int32,UInt32,Int64,UInt64。它们都是Int家族中的成员。它们的区别在于能够存储的字节数不同。字节越多,可以存储的值也越大。实际上你很少会遇到他们,一般都是用Int,默认使用8个字节,可以存储正整数和负整数,其中最大的数为19位数。这已经是个天文数字了。

    CGFloat,这并不是一个Swift类型,而是iOS SDK定义的类型。它和Float以及 Double类似。由于历史原因,它的使用贯穿UIKit。(CG前缀代表Core Graphics框架)

    Swift对类型的要求非常严格,比其他语言要严格的多。如果变量的类型是Int,你就不能将Float型的值放进去。反过来也不行。(有些语言可以把Int放入Float)

    即使Int和Float都是存放数字,Swift也不会自动对它们进行转换,你需要手动执行类型转换。

    例如:

    var i = 10
    var f: Float
    f = i //❌
    f = Float(i) //✅
    

    当你创建一个变量时,你并不需要总是指定它的类型。如果你给一个变量初始值的话,Swift会进行类型推断,自己判定它的类型:

    var i = 10    //Int
    var d = 3.14  //Double
    var b = true  //Bool
    var s = "Hello, world"  //String
    

    Swift会根据初始值自动判定类型。

    注意一下,对于小数3.14,Swift会将其的类型判定为Double,而不是Float。如果你要使用Float,那么你必须指明类型:

    var f: Float = 3.14
    

    上面的: Float,叫做类型注释。使用了类型注释的变量,Swift不再对其进行类型判定。

    同样的,如果你想创建一个名称为i的Doubel型变量,你可以这样写:

    var i: Double = 10
    

    或者:

    var i = 10.0
    

    上面的10,3.14以及“Hello World”称作字面值,它们在创建基础类型(Int,Double,String等)时非常有用。但是对于复杂的类型,你就需要先实例化一个对象。

    当你写出下面的语句时,

    var item: ChecklistItem
    

    仅仅是通知了Swift,你想要存储一个ChecklistItem对象到item变量中,但是此时不会创建ChecklistItem对象,为此你又写了一句:

    item = ChecklistItem()
    

    这是一个预留内存,用来保存对象的数据,在调用init()后将对象准备好,随时可以使用。预留内存也称为资源配置,然后使用对象的初始值填满它就叫做初始化。

    这整个过程就叫做对象的实例化,你创建了一个对象的实例。实例就是一块保存着对象中的变量的值的内存。(这就是为什么对象中的变量被称为实例变量,get到了吗?)

    当然,你可以将上面的两行合并为一行:

    var item = ChecklistItem()
    

    这里你省略了“: ChecklistItem”这一类型注释,因为Swift非常聪明,它可以识别出item的类型就是ChecklistItem。

    然而,你并不能把()这对括号省略掉,这对括号的作用就是通知Swift,你要创建一个新的ChecklistItem实例。

    有些对象允许你在这对括号内传递参数,例如:

    var item = ChecklistItem(text: "Charge my iPhone", checked: false)
    

    这里是调用了相应的init(text,checked)方法来分配新的空间存储ChecklistItem对象。

    目前为止,你已经见过了两种类型的变量:局部变量(local variables),生命期仅在方法调用期间;实例变量,属于整个对象,因此可以在任何方法内被调用。

    变量的生命期也称为变量的作用范围。局部变量的作用范围比实例变量小的多。一旦方法调用结束,局部变量就被释放掉了。

    class MyObject {
      var count = 0   // 一个实例变量
      func myMethod() {
        var temp: Int   // 一个局部变量
    temp = count  // 方法内调用了实例变量
    // 局部变量temp在方法外不存在
     }
    }
    

    当实例变量与局部变量重名时,局部变量会覆盖实例变量,你需要避免这种情况的发生,因为这样可能会因为调用错变量而引发bug,比如:

    class MyObject {
      var count = 7  // 一个实例变量
      func myMethod() {
        var count = 42  // 局部变量覆盖了实例变量
        print(count)   // 打印值为42
    } 
    }
    

    有些开发者会在实例变量的名称前面放一个下划线来避免这个情况,比如:_count。另外一个方法就是使用关键字self来调用实例变量。

    class MyObject {
      var count = 7  // 一个实例变量
      func myMethod() {
        var count = 42  // 局部变量覆盖了实例变量
        print(self.count)   // 打印值为 7
    } 
    }
    

    变量不仅仅是存储值而已。一个变量是用来存放值的容器,它可以被正在运行中app改变。

    例如:记事本类型的app可以允许用户随意的改变其中的文本,所以你要将这些文本存储到String型变量中,每次用户编辑这些文本的时候,变量的值都会被更新。

    通常,你仅仅是想存储一个计算结果或者一个方法的返回值,并且不想再改变它们。这种情况,你最好就不要使用变量,而是使用常量。

    以下值在设置好以后,永远不会发生变化:

    let pi = 3.141592
    let difference = abs(targetValue - currentValue)
    let message = "You scored \(points) points"
    let image = UIImage(named: "SayCheese")
    

    如果一个常量是属于某个方法的局部常量,那么每次这个方法被调用时,允许给这个常量一个新的值,只是在方法的内部,这个值不允许被改变,因为方法被调用结束后,这个常量就被释放掉了,所以每次调用方法时,这个常量可以被给予一个不同的值。

    小贴士:我的建议时,你始终使用let,尽量不要使用var,直到编译器报错,通知你需要修改为var的时候再用var。

    基础的变量类型,比如整数和字符串,被称为值类型,使用let创建的常量,仅可以被赋值一次:

     let pi = 3.141592
    pi = 3 // 不允许这样操作
    

    然而,对象都是引用类型的,使用常量实例化对象时,仅仅是将到这个对象的引用常量化了,而对象本身还是可以发生变化的。

    let item = ChecklistItem()
    item.text = "Do the laundry"
    item.checked = false
    item.dueDate = yesterday
    

    但是不允许像下面这样操作:

     let anotherItem = ChecklistItem()
    item = anotherItem    //不允许这样操作
    

    那么你怎么知道哪些东西是值类型,哪些东西是引用类型呢?

    使用class关键字定义的都是引用类型,而struct或者enum定义的对象都是值类型。实际上,大多数来自iOS SDK中的对象都是引用类型,而Swift内建的Int,String,Array等都是值类型。

    一个变量仅能存储一个值。要存储多个对象的话,需要使用一种叫做集合对象的东西。比如数组(Array)或者字典(Dictionary),这些之前你都见过了。

    一个数组存储一列对象。存储的对象在数组中有序的排列着,你可以用过数组的索引访问它们。

    // an array of ChecklistItem objects:
    var items: Array<ChecklistItem>
    // using shorthand notation:
    var items: [ChecklistItem]
    // making an instance of the array:
    items = [ChecklistItem]()
    // accessing an object from the array:
    let item = items[3]
    

    数组可以写作Array<Type>或者[Type]。第一个是官方的名称,第二个称为语法糖,它更容易阅读。一些其他的语言中数组会写作Type[]。

    字典可以存储配对的“键-值”。通常键是String型的,而值是其他类型。

    // a dictionary that stores (String, Int) pairs, for example a
    // list of people’s names and their ages:
    var ages: Dictionary<String, Int>
    // using shorthand notation:
    var ages: [String: Int]
    // making an instance of the dictionary:
    ages = [String: Int]()
    // accessing an object from the dictionary:
    var age = dict["Jony Ive"]
    

    从数组中检索对象的符号和数组非常类似,都是使用[]方括号。但是检索数组中的对象时,使用的是正整数,而字典则使用的是字符串。

    还有一些其他类型的集合类型,但是数组和字典是用的最多的。

    数组和字典也被称为泛型,这意味这它们独立于你存储到其中的对象的类型。

    你可以用一个数组存储Int型对象,String对象,甚至另一个数组。

    这就是为什么你在使用是一个数组前,要指明存储到这个数组中的对象的类型,你不能像下面这样存储数组:

    var items: Array  // error: should be Array<TypeName>
    var items: []     // error: should be [TypeName]
    

    (最近😷了,翻译的进度就慢一些,后面会补上,希望大家耐心等待)

    相关文章

      网友评论

        本文标题:iOS Apprentice中文版-从0开始学iOS开发-第二十

        本文链接:https://www.haomeiwen.com/subject/ilsslxtx.html