iOS Programming: The Big Nerd Ranch Guide 5th Edition

1- Introduction

作为iOS开发者,必须掌握:

  • swift 语言
  • 重要概念 - 代理,归档等
  • 框架

MVC pattern

MVC.png

Interface Builder

  • document outline
  • canvas
IB.png

Xcode utility area

utility area.png

scheme

scheme.png

Auto Layout

autolayout.png
  • 技巧:多选添加多个约束
multiple.png

Application Icons

  • size

Launch Screen

launch.png

2 - The Swift Language

Types

  • structures
  • classes
  • enumerations
types.png
  1. 字典的key必须是hashable,保证key唯一,Int, Float, Character, String都是hashable
  2. 集合的元素唯一并且是hashable
  3. literal values 是 instance。
  4. 初始化器:
  • 空值
l​e​t​ ​e​m​p​t​y​S​t​r​i​n​g​ ​=​ ​S​t​r​i​n​g​(​)​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​// "​"​
l​e​t​ ​e​m​p​t​y​A​r​r​a​y​O​f​I​n​t​s​ ​=​ ​[​I​n​t​]​(​)​ ​ ​ ​ ​ ​ ​ ​// 0​ ​e​l​e​m​e​n​t​s​
l​e​t​ ​e​m​p​t​y​S​e​t​O​f​F​l​o​a​t​s​ ​=​ ​S​e​t​<​F​l​o​a​t​>​(​)​ ​ ​// 0​ ​e​l​e​m​e​n​t​s​”
  • 默认值
l​e​t​ ​d​e​f​a​u​l​t​N​u​m​b​e​r​ ​=​ ​I​n​t​(​)​ ​ // 0​
l​e​t​ ​d​e​f​a​u​l​t​B​o​o​l​ ​=​ ​B​o​o​l​(​)​ ​ ​   // f​a​l​s​e​
le​t​ ​d​e​f​a​u​l​t​F​l​o​a​t​ ​=​ ​F​l​o​a​t​(​)​   // 0.0
  1. 文档资料

3 - Views and the View Hierarchy

  1. View Hierarchy
view hierarchy.png
  1. 一旦层级关系确定,就开始绘制到屏幕上:
  • 绘制自己到layer
  • 所以的layer在屏幕上组合起来
render.png
  1. 一个 framework 是相关的类和资源的集合。

  2. 一个视图的frame是相对其父视图的。

  3. Baseline :大部分是和bottom相同的,但是UITextField和UILabel是在其内容的底部。

  4. Frame 和 Alignment rectangle

  • Alignment rectangle 大部分和 Frame相同,但它是通过约束计算出来的。
alignment.png

4 - Text Input and Delegation

  1. 使用N​S​N​u​m​b​e​r​F​o​r​m​a​t​t​e​r 输出格式化数字的字符串
    参考 iOS中数字的格式化 NSNumberFormatter
l​e​t​ ​n​u​m​b​e​r​F​o​r​m​a​t​t​e​r​:​ ​N​S​N​u​m​b​e​r​F​o​r​m​a​t​t​e​r​ ​=​ ​{​
 ​ ​ ​ ​l​e​t​ ​n​f​ ​=​ ​N​S​N​u​m​b​e​r​F​o​r​m​a​t​t​e​r​(​)​
 ​ ​ ​ ​n​f​.​n​u​m​b​e​r​S​t​y​l​e​ ​=​ ​.​D​e​c​i​m​a​l​S​t​y​l​e​
 ​ ​ ​ ​n​f​.​m​i​n​i​m​u​m​F​r​a​c​t​i​o​n​D​i​g​i​t​s​ ​=​ ​0​
 ​ ​ ​ ​n​f​.​m​a​x​i​m​u​m​F​r​a​c​t​i​o​n​D​i​g​i​t​s​ ​=​ ​1​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​f​
}​(​)​

  c​e​l​s​i​u​s​L​a​b​e​l​.​t​e​x​t​ ​=​ ​n​u​m​b​e​r​F​o​r​m​a​t​t​e​r​.​s​t​r​i​n​g​F​r​o​m​N​u​m​b​e​r​(​v​a​l​u​e​)​
  1. 代理:处理多个callback。

  2. 代理协议的命名:类名+Delegate

  3. 避免输入两个.

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        
        let existingTextHasDecimalSeparator = textField.text?.rangeOfString(".")
        let replacementTextHasDecimalSeparator = string.rangeOfString(".")
        
        if existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil {
            return false
        }
        else {
            return true
        }
    }
  1. 限制只能输入特定的字符 ???

5 - View Controllers

  1. 当一个View controller 被设置为window的 rootViewController ,它的view 被添加到window的视图层级中。

  2. A UITabBarController’s view is a UIView with two subviews: the tab bar and the view of the selected view controller

  3. 生命周期

  • init(coder:) :用 storyboard 创建 或 init(nibName:bundle:):用代码创建
  • loadView()
  • viewDidLoad()
  • viewWillAppear(_: )
  • viewDidAppear(_:)
  • viewWillDisappear(_:)
  • viewDidDisappear(_:)

6 - Programmatic Views

  1. Constraints Common ancestor
  • 约束需要加到最近的共同祖先上。
  • active属性的作用:先找共同祖先,然后调用addConstraint(:) 或 removeConstraint(:)
  1. layoutGuide
  • 使用topLayoutGuide:让内容不遮挡status bar 或 navigation bar
  • 使用bottomLayoutGuide: 让内容不遮挡 tab bar
  • 可以使用的约束:topAnchor, bottomAnchor, heightAnchor
  1. layoutMarginsGuide
  • 每个视图都有这个属性

7 - Localization

国际化

  1. NSNumberFormatter
  • 有一个local属性,被设置为设备的当前local,使用NSNumberFormatter时,它会首先检查它的local属性,然后格式化设置内容。
  • NSLocal 知道不同地区使用的symbols, dates, decimals and whether use metric system。
@IBAction func fahrenheitFieldEditingChanges(textField: UITextField) {
        if let text = textField.text, let number = numberFormatter.numberFromString(text) {
            fahrenheitValue = number.doubleValue
        }
        else {
            fahrenheitValue = nil
        }
    }
  • NSLocal.currentLocal() 代表用户设置的地区。
 func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        
        let currentLocale = NSLocale.currentLocale()
        let decimalSeparator =
        currentLocale.objectForKey(NSLocaleDecimalSeparator) as! String
        
        let existingTextHasDecimalSeparator = textField.text?.rangeOfString(decimalSeparator)
        let replacementTextHasDecimalSeparator = string.rangeOfString(decimalSeparator)
        
        if existingTextHasDecimalSeparator != nil && replacementTextHasDecimalSeparator != nil {
            return false
        }
        else {
            return true
        }
    }

本地化

NSLocal 没有的,只有本地化。

  1. 调试技巧:
  • Edit Scheme... -> Run -> Options -> Application Region
  • Show Assistant Editor -> Preview -> 右下角可以选择语言
  • 添加本地化后 -> 删除应用 -> 重启xcode -> Clean -> Run
  1. 本地化的原理:main bundle 里放了所有的资源文件,本地化资源文件就是复制一份放在 bundle 的具体语言的目录下(目录名为语言和地区的简写,后缀为iproj)。当需要资源的时候,先查找bundle的最顶层,如果找到,返回文件的URL,如果没有找到,则根据地区和语言设置找到相应的iproj目录,查找需要的资源,如果找到,返回URL,如果没有找到,则查找Base.iproj目录,如果没有找到,则返回nil。(为什么使用调试技巧3)
  • storyboard 怎么办?Xcode 创建了 Base.iproj 目录将storyboard 放在里面,如需本地化只需创建 Localizable.strings 文件(只本地化字符串)。
  • 界面可以通过Autolayout本地化。
  1. storyboard 本地化步骤

  2. 选中右侧Localization -> 选中English


    check.png
  3. Project -> Info -> + -> 添加其他语言

  4. 翻译对应语言

  5. 使用调试技巧2,选择Application Language

  6. 代码中的字符串本地化步骤

  7. 在代码中使用NSLocalizedString函数

let str = NSLocalizedString("hello world", comment: "xxxxddd")
  1. 用终端进入代码文件目录

  2. 输入命令:genstrings ViewController.swift

  3. 将生成的文件Localizable.strings拖入项目

  4. 选中右侧Localization -> 点击Localize... -> Base

  5. 勾选其他语言

  6. 翻译对应语言

  7. 使用调试技巧2,选择Application Language

  8. 使用XLIFF文件

  9. 导出:Project -> Editor -> Export For Localization...

  10. 翻译

  11. 导入:Project -> Editor -> Import For Localization...

8 - Controlling Animations

  1. 约束动画:layoutIfNeeded -> 修改动画 -> layoutIfNeeded
    技巧:使用swap(_:)函数。

     view.layoutIfNeeded()
     
     let screenWidth = view.frame.width
     self.nextQuestionLabelCenterXConstraint.constant = 0
     self.currentQuestionLabelCenterXConstraint.constant += screenWidth
     
     UIView.animateWithDuration(0.5,
         delay: 0,
         options: [.CurveLinear],
         animations: {
             self.currentQuestionLabel.alpha = 0
             self.nextQuestionLabel.alpha = 1
             
             self.view.layoutIfNeeded()
         },
         completion: { _ in
             swap(&self.currentQuestionLabel, &self.nextQuestionLabel)
             swap(&self.currentQuestionLabelCenterXConstraint, &self.nextQuestionLabelCenterXConstraint)
             
             self.updateOffScreenLabel()
     })
    

9 - TableViewControllers

  1. UITableViewController
  • view controller
  • data source
  • delegate


    TableViewController.png
  1. MVC


    MVC.png
  • 使用依赖注入:初始化传入(纯代码)或者通过属性传入(stroyboard)。
  1. UITableViewCell 层级关系


    UITableViewCell.png

    style.png
  2. 重用机制

  • 注册
  • dequeue
    首先根据identifier查看重用池(队列),如果有,则dequeue,如果没有,系统根据注册的类型创建。
  1. Content Insets
    // Get the height of the status bar
    let statusBarHeight = UIApplication.sharedApplication().statusBarFrame.height

     let insets = UIEdgeInsets(top: statusBarHeight, left: 0, bottom: 0, right: 0)
     tableView.contentInset = insets
     tableView.scrollIndicatorInsets = insets
    
  2. 设计模式

  • Delegation
  • Data source
  • Model - View - Controller
  • Target - action pairs
  1. dynamic type
  • storyboard(静态)


    dynamic type.png

    dynamic type2.png
  • code (动态)
    func updateLabels() {
        let bodyFont = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
        nameLabel.font = bodyFont
        valueLabel.font = bodyFont
        
        let caption1Font = UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1)
        serialNumberLabel.font = caption1Font
    }
  1. stack view
    技巧:nested stackView + intrinsic size + content hugging priority + content compression resistance priority + auto layout

10 - NavigationController

  1. 触发segue
  • action item(button, tableViewCell, other UIControl) :storyboard 触发
  • identifier : 代码触发,判断segue
  • 传值: tableView.indexPathForSelectedRow
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // If the triggered segue is the "ShowItem" segue
        if segue.identifier == "ShowItem" {
            
            // Figure out which row was just tapped
            if let row = tableView.indexPathForSelectedRow?.row {
                
                // Get the item associated with this row and pass it along
                let item = itemStore.allItems[row]
                let detailViewController = segue.destinationViewController as! DetailViewController
                detailViewController.item = item
            }
        }
    }
  1. endEditing(_:):关闭键盘的便利方法。
  • 不用关心哪一个 text field 是响应者。它会检查自己层级中是否有是第一响应者的 text field,如果有,则对其调用 resignFirstResponder()
  • 重写becomeFirstResponder(),自定义 text field 的 borderStyle
  1. 每一个UIViewController 有一个 navigationItem 属性,它不是UIView的子类,而是为navigation bar 提供内容。navigation bar 根据 view controller 的navigationItem 配置自己。UIBarButtonItem 类似。
title.png
everything.png

11 - Camera

  1. UIImagePickerController
    设置 sourceType
  • Camera:使用相机
  • PhotoLibrary:使用相册
  • SavedPhotosAlbum:使用最近拍照


    source type.png
    @IBAction func takePicture(sender: UIBarButtonItem) {
        
        let imagePicker = UIImagePickerController()
        
        // If the device has a camera, take a picture, otherwise,
        // just pick from photo library
        if UIImagePickerController.isSourceTypeAvailable(.Camera) {
            imagePicker.sourceType = .Camera
        }
        else {
            imagePicker.sourceType = .PhotoLibrary
        }
        
        imagePicker.delegate = self
        
        // Place image picker on the screen
        presentViewController(imagePicker, animated: true, completion: nil)
    }

    func imagePickerController(picker: UIImagePickerController,
        didFinishPickingMediaWithInfo info: [String: AnyObject]) {
            
            // Get picked image from info dictionary
            let image = info[UIImagePickerControllerOriginalImage] as! UIImage
            
            // Store the image in the ImageStore for the item's key
            imageStore.setImage(image, forKey:item.itemKey)
            
            // Put that image onto the screen in our image view
            imageView.image = image
            
            // Take image picker off the screen -
            // you must call this dismiss method
            dismissViewControllerAnimated(true, completion: nil)
    }
  1. Cache
    将image 存储在内存,如果收到low-memory 通知,则释放占有的资源。

12 - Archiving

  1. 界面文件的对象是arhieved。
    数字使用:encodeInt(intv: Int32, forKey key: String)
    字符串使用:encodeObject(objv: AnyObject?, forKey key: String)
    原因:虽然String不是对象,但是它和NSString 桥接了,会自动转换。

  2. Encoding 是递归操作
    所有对象都必须遵守NSCoding协议!


    encoding.png
  3. 沙盒
    Library/Preferences 是自动被NSUserDefaults管理。


    sandbox.png
  4. 使用DocumentDirectory和first的原因
    iOS和OS X共用一套API。
    let itemArchiveURL: NSURL = {
    let documentsDirectories =
    NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory,
    inDomains: .UserDomainMask)
    let documentDirectory = documentsDirectories.first!
    return documentDirectory.URLByAppendingPathComponent("items.archive")
    }()

  5. application state

state.png
conclude.png
  1. image data
    UIImageJPEGRepresentation(image, 0.5)
    第二个参数为压缩质量。1.0 是最高质量,少压缩。
    data.writeToURL(imageURL, atomically: true)
    第二个参数如果为true,文件先写到文件的临时地方,一旦写操作完成,文件重命名为URL,替换以前存在的文件。

  2. bundle
    bundle中的文件是只读的,不能修改,在运行时也不能动态添加文件。

13 - SizeClasses

  1. 改变sizeClasses可以改变:
  • 视图的属性 (比如stackView)
  • subview 是否 installed
  • constraint 是否 installed
  • constraint 的 constant
  • subview 的字体

14 - TouchEvents
1.默认状态,view一次只能接受一个touch。一旦开始,其他touch的touch生命周期方法ignored。

multiple touch.png
  1. UIMenuController
  • only one UIMenuController per application
  • has list of UIMenuItem
  • item has a title and action
  • action message sends the first responder of the window
    func tap(gestureRecognizer: UIGestureRecognizer) {
       print("Recognized a tap")
       
       let point = gestureRecognizer.locationInView(self)
       selectedLineIndex = indexOfLineAtPoint(point)
       
       // Grab the menu controller
       let menu = UIMenuController.sharedMenuController()
       
       if selectedLineIndex != nil {
           
           // Make ourselves the target of menu item action messages
           becomeFirstResponder()
           
           // Create a new "Delete" UIMenuItem
           let deleteItem = UIMenuItem(title: "Delete", action: "deleteLine:")
           menu.menuItems = [deleteItem]
           
           // Tell the menu where it should come from and show it
           menu.setTargetRect(CGRect(x: point.x, y: point.y, width: 2, height: 2),
               inView: self)
           menu.setMenuVisible(true, animated: true)
       }
       else {
           // Hide the menu if no line is selected
           menu.setMenuVisible(false, animated: true)
       }
       
       setNeedsDisplay()
   }
   
   func deleteLine(sender: AnyObject) {
       // Remove the selected line from the list of finishedLines
       if let index = selectedLineIndex {
           finishedLines.removeAtIndex(index)
           selectedLineIndex = nil
           
           // Redraw everything
           setNeedsDisplay()
       }
   }
   
   override func canBecomeFirstResponder() -> Bool {
       return true
   }

14 - CollectionView

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

推荐阅读更多精彩内容