Swift学习笔记2 - Views

作者: 诸葛俊伟 | 来源:发表于2016-05-07 10:06 被阅读838次

    前言

    这是斯坦福大学在线课程-Developing iOS 9 Apps with Swift 的学习内容,在iTunes上就可以搜到,有兴趣的小伙伴可以一起学习一起进步哈!这里我将一些我认为的关键知识点摘录在我的学习笔记系列中。之前用英文写了几篇,感觉没什么人看,其实个人认为用英文学再用英文写其实更简单方便一些。

    Demo

    首先,是一张萌蠢的笑脸,你要是看着不舒服,你可以从我的github中下载相应代码,或者自己敲哈,让它哭也成。

    smile

    Views

    • 先乱入一条(别打我),swift的命名规范:在swift中,所有类型的名称的首字母大写。
      比如private enum Eye, private struct Ratios
    • 一个view(比如:UIView)表示的是一个矩形的区域,定义了一个坐标空间。它可以用来画图(比如这个demo),也可以用来处理点击事件。
    • view在swift中是分级的,也就是说
    • 一个view只能有一个superview, var superview: UIView?
    • 一个view可以有很多个subview, var subviews: [UIView]
    • subview的范围甚至可以在他老爸view的范围之外。
    • view是有顺序之分的,后来者居上。
    • UIWindow在view(分级)中处在非常高级的位置,在一个app中一般只有一个。

    初始化一个UIView

    有两种不同的方式:

    1. init(frame: CGRect) // 由代码创建生成
    2. init(coder: NSCoder) // 由storyboard生成

    如果你需要一个初始化程序,那这两种初始化都需要执行:

    func setup() { ... }
    
    override init(frame: CGRect) { // a designed initializer
      super.init(frame: frame) 
      setup()
    }
    
    required init(coder aDecoder: NSCoder) { // a required initializer
      super.init(coder: aDecoder)
      setup()
    }
    

    另一个初始化方式:使用awakeFromNib(),这只能用于storyboard上创建的UIView。注意这不是一个初始化器,它是“初始化完成之后立马被调用”。

    坐标系统数据结构

    • CGFloat
      在UIView的坐标系统中,使用CGFloat类型的数据而不是Double或者Float。你可以将Double或者Float转换成CGFloat, 比如: let cgf = CGFloat(aDouble)
    • CGPoint
      比如:var point = CGPoint(x: 37.0, y: 52.0)
      其实就是由两个CGFloat类型的数组成的一个简单结构。
    • CGSize
      和CGPoint差不多,也是由两个CGFloat类型的数组成的简单结构。
      比如:
    var size = CGSize(width: 100.0, height: 50.0)
    size.width += 10
    size.height += 5
    
    • CGRect
      由CGPoint和CGSize组成的结构
    struct CGRect {
      var origin: CGPoint
      var size: CGSize
    }
    let rect = CGRect(origin: aCGPoint, size: aCGSize) //当然可以用别的初始化方式
    

    这结构里自带了很多好东西:

    • var minX: CGFloat //最小的x

    • var midY: CGFloat //中间点的y

    • intersects(CGRect) -> Bool //判断和别的CGRect有没有相交

    • intersect(CGRect) //返回相交的部分

    • contains(CGRect) -> Bool //判断是否包含了另一个CGRect

    • View的坐标系统

    • 左上是初始点。如下图:


    • 注意,单位是,不是像素,点不是像素,点等于几个像素。比如在6 plus上,每个点有3个像素;有些手机则是2个像素一个点。
      你可以通过这条语句知道你的设备一个点有多少像素,var contentScaleFactor: CGFloat

    • 当你需要画图的时候,使用var bounds: CGRect,这是一个包含了你自己的坐标系统的画图空间的矩形,也就是说,它在你自己的画图空间里,由你在view中的代码实现来定义bounds.origin。

    • 那么UIView在哪里?

    var center: CGPoint // UIView的中心坐标(在它的superview的坐标系统中)
    var frame: CGRect // 包含UIView的矩形(在它的superview的坐标系统中)
    

    。 frame指的是你的view在superview中的位置,也就是说这是放在superview的坐标系统中的,而不是你的画图坐标系统中。
    。 center相应的,也是指的superview中的center,和你的画图空间并没有毛线关系~当你画图的时候用bounds就好啦。看代码理解也许会比较清晰一点,可以以上面提到的demo为例,
    。 frame和bounds的宽高也不同,因为view可以旋转。

    创建一个view

    在上面提到的demo中,我们做的是一个笑脸,而iOS并没有smileview这个东西,所以我们要自己建。我们使用的是一般view(generic view),就当是一般类吧,可以拿来随意发挥。
    你可以在object library里轻松找到它,或者搜“view“
    e.g.

    // 假设这段代码在UIViewController中
    let labelRect = CGRect(x: 20, y: 20, width: 10, height: 10)
    let label = UILabel(frame: labelRect) // UILabel是UIView的子类,所以那些UILabel中的那些字也是一笔一画画出来的。。
    label.text = "hello"
    view.addSubview(label)
    

    自定义Views

    • 何时需要自定义UIView的子类

    • 我想自定义绘图

    • 我想以一些特殊的方式处理点击事件(这里不同于button或者slider)

    • 至于画图,只需要创建一个UIView的子类,然后override drawRect:

    • override func drawRect(regionThatNeedsToBeDrawn: CGRect)

    • 如之前所说,你可以画在regionThatNeedsToBeDrawn的范围之外。
      - UIView的边界定义了我们整个画图区域,region只是一个子区域

    • !!!绝对不能直接调用drawRect,它是系统的专属天使。但是当你需要画图怎么办呢?告诉系统,你要用这个方法了,用以下的语句:

    setNeedsDisplay()
    setNeedsDisplayInRect(regionThatNeedsToBeRedrawn: CGRect)
    

    iOS会在一个适当的时机调用drawRect,比如你的破事全部决定了之后,一次性将要重画(redraw)的东西全部drawRect。

    • 那么我怎么实现drawRect呢?

    • 你可以使用一个类C的API(不是面向对象的),叫Core Graphics,swift是完全面相对象的,掺和进来这玩意应该不是好事吧 ==

    • 你还可以使用面向对象的类-UIBezierPath,这也是我们在demo中所使用的。

    • Core Graphics的一些基本概念

    1. 使用UIGraphicsGetCurrentContext()来获取一些能在drawRect中使用的文本内容(context,打印的,屏幕外的缓存中的, etc.)
    2. 创建路径(线,圆)
    3. 设定一些相关属性,比如颜色,字体,线宽之类的。
    4. stroke或者fill以上创建的路径。
    • UIBezierPath
      和上面那个家伙差不多,只是UIBezierPath自动知道context。

    • 定义一个路径

    • 创建一个UIBezierPath

    let path = UIBezierPath()
    
    • 加几条线,或者圆弧
    path.moveToPoint(CGPoint(x: 80, y: 50))
    path.addLineToPoint(CGPoint(x:140, y: 150))
    path.addLineToPoint(CGPoint(x: 10, y: 150))
    
    • 可以闭合路径
    path.closePath()
    

    此时就已经得到了一个可爱的三角形。但是并没有画出来

    • 注意, 你仅仅将上面创建的线条放在drawRect中,并不会显示什么。必须要设置相应的属性(颜色,线宽等等),然后strokefill,才能在屏幕上显示出来。
    UIColor.greenColor().setFill() // 注意这是UIColor中的方法
    UIColor.redColor().setStroke() // 注意这是UIColor中的方法
    path.lineWidth = 3.0 // 这是UIBezierPath中的属性
    path.fill() // UIBezierPath中的方法
    path.stroke() // UIBezierPath中的方法
    
    • 你也可以用UIBezierPath画很多其它类型的图,比如:
    let roundRect = UIBezierPath(roundedRect: CGRect, cornerRadius: CGFloat)
    let oval = UIBezierPath(ovalInRect: aCGRect)
    
    • 也可以圆滑夹角,就是给每个角增加一个弧度。使用addClip()
    • 也可以进行碰撞监测,就是检测一个点是不是在闭合路径里面。
    func containsPoint(CGPoint) -> Bool
    

    UIColor

    • 对于很多普通颜色来讲,有很多类方法可以使用,比如let green = UIColor.greenColor(),可以用RGB, HSB, 甚至可以使用某些样式(比如图片)。
    • UIView的背景色,var backgroundColor: UIColor
    • 颜色可以有alpha,可以设置透明度
    let transparentYellow = UIColor.yellowColor().colorWithAlphaComponent(0.5) // 这是个instance method, 不是type method
    

    alpha: 0.0(完全透明) - 1.0(完全不透明)

    • 如果想要在程序中设置颜色的透明度,必须设置var opaque = false, 来让系统知道这些都不是不透明的,也就是你要开始设置透明度了。

    • 也可以设置整个UIView的透明度

    var alpha: CGFloat
    

    通过alpha,可是设计一个渐出(慢慢消失)的动画。

    • 也可以隐藏一个view,通过var hidden: Bool

    显示(画)文本

    • 一般我们使用UILabel来显示文本,但我们有时候还是需要在drawRect中直接显示文本
    • 在drawRect中,可以使用NSAttributedString
      AttributedString就是每个string中的字符,都有一个字典存储相应的每个字符的属性信息,比如颜色,字体等。
    let text = NSAttributedString("hello")
    text.drawAtPoint(aCGPoint)
    let textSize: CGSize = text.size //这个字符串需要多少空间
    
    • NSAttributedString的两个弊端
    1. 我们知道,var具有可变性(mutability),let没有。但是在NSAttributedString中,不管是var还是let,都是不可变的(immutable)。因为这是个objective-c的类,所以swift中还是有点缺陷。要想做到可变形,你可以使用另一个类,NSMutableAttributedString,如下语句:
    let mutableString = NSMutableAttributedString("some text")
    

    注意,NSAttributedString不是String,也不是NSString.

    1. swift的string是unicode的,比oc的强大太多,所以它们的string也不一样,在NSAttributedString中,需要使用NSRange,注意这不是Range,这是oc里的NSRange。所以在使用的时候,需要将range转换成NSRange(可自动完成),然后才能在NSAttributedString中用NSRange。
    func setAttributes(attributes: Dictionary, range: NSRange)
    func addAttributes(attributes: Dictionary, range: NSRange)
    

    在Attributes中可以放以下这些dictionary:
    NSForegroundColorAttributeName: UIColor
    NSStrokeWidthAttributeName: UIFloat
    NSFontAttributeName: UIFont

    字体

    字体在苹果的产品中占有非常重要的地位,毕竟艺术品。

    • 使用字体的最好的方式
    • 对于文本内容,采用preferred font
    static func preferredFontForTextStyle(UIFontTextStyle) -> UIFont
    UIFontTextStyle.Headline
    UIFontTextStyle.Body
    UIFontTextStyle.Footnote
    
    • 对于按钮之类的,使用系统字体(system fonts)
    static func systemFontOfSize(pointSize: CGFloat) -> UIFont
    static func boldSystemFontOfSize(pointSize: CGFloat) -> UIFont
    

    在用户的文本信息中,不要使用这些系统字体

    • 其它方式:UIFont和UIFontDescriptor

    显示图片

    有个UILabel相类似的类,UIImageView,当然你还是有可能想在drawRect中直接显示图片的,那么。。。

    • 创建UIImage - let image: UIImage? = UIImage(named: "foo"),optional是因为有可能没有图片
    • 从系统文件中创建(files in the file system)
    let image: UIImage?  = UIImage(contentsOfFile: aString)
    let image: UIImage?  = UIImage(data: anNSData) // jpg, png, tiff, etc.
    
    • 开始画
    let image: UIImage = ...
    image.drawAtPoint(aCGPoint) // 图片的左上角
    image.drawInRect(aCGRect) // 将图片按比例扩充到aCGRect中
    image.drawAsPatternInRect(aCGRect) //将图片平铺到aCGRect中
    

    欢迎转载,转载请注明出处。访问我的个人主页,了解更多。

    相关文章

      网友评论

        本文标题:Swift学习笔记2 - Views

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