26
2017
09

IOS开发之多重MVC以及手势

IOS开发之多重MVC以及手势

继续学习IOS开发,这一次学习的是多重MVC架构以及手势识别。

把之前的View接口化

首先需要把之前的FaceIt程序的可以改变的参数单独提取出来。

//changable parameters

    //1 means smile and -1 means sad.
    var smileFactor: CGFloat = 1 
    var eyeOpen: Bool = false
    var eyeBrowTiltFactor: CGFloat = -1
    var lineColor: UIColor = UIColor.black

如果想要在main.storyboard里面实时可以看到view里面的情况,只需要在类前面加入如下的一行代码。
这里写图片描述

同样的,可以使用以下的放下把这些参数放到Interface里面。
这里写图片描述

这样子的话就可以在Storyboard的侧面的边栏里面了。
这里写图片描述

但是这个时候会发现,如果在FaceView里面直接改变变量的初始值,Storyboard的图像并不会重新渲染。要改变这个,需要使用变量的didSet属性,意思是如果变量在某种程度上更改了,那么就需要执行dieSet括号内的内容。

var smileFactor: CGFloat = 1 { didSet{setNeedsDisplay()}}

Model部分

接下来我们将会完成MVC中model的部分,我把它命名为FacialExpressionModel。这个时候问题来了,是使用class呢,还是使用struct呢?因为看到stanford的老师在这个demo里面使用了struct。后来查阅了相关的资料,发现了它们根本的区别在于:

struct传值,class传引用

结构(struct)是一种值类型。也就是说,结构实例是分配在线程堆栈上的,结构本身是包含有值的,而不是像类一样的引用类型,包含的是所指向堆当中的引用(指针)。也就是说,结构的生存周期与简单类型(int,double等)相同的。

你使用的是一个对class实例的引用。而你使用的不是对一个struct的引用。(而是直接使用它们)

当我们将class作为参数传给一个方法,我们传递的是一个引用。struct传递的是值而非引用。

紧接着会有一个让人非常困惑的问题,到底model写的内容是什么呢?其实目前来看,我们的这个小应用都只是在控制view,所以目前只需要用到view和viewcontroller,那么model只需要模拟把各种情况写出来就好了(计算机的是结果,因为要显示的不是情况)。

model返回的是view的各种情况类型,一般用enum。

关于enum有以下两点需要注意:
1. relative值的使用
可以如下方式定义relative值,然后如下方式使用。

//定义
enum Barcode {
  case UPCA(Int, Int, Int)
  case QRCode(String)
}

//使用
switch productBarcode {
case .UPCA(let numberSystem, let identifier, let check):
    println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case .QRCode(let productCode):
    println("QR code with value of \(productCode).")
}
  1. rawValue的相关使用
    作为相关值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。
enum ASCIIControlCharacter: Character {
    case Tab = "\t"
    case LineFeed = "\n"
    case CarriageReturn = "\r"
}

原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。

enum Planet: Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

let possiblePlanet = Planet(rawValue: 7)
//这里就是possiblePlanet就是Uranus

关于这些类型的切换,有一个要处理的问题就是边界问题。比如如果我要从伤心、平静、开心三种状态之间切换,【更开心】这个操作有可能会向上跳一级,或者什么都不发生。以下的代码可以轻易的解决这个问题。

 func moreSmile() -> mouth{
     return  mouth(rawValue: rawValue + 1) ?? .smile
 }

 func moreSad() -> mouth{
     return mouth(rawValue: rawValue - 1) ?? .sad
 }

这里插播一个swift的命名规范。

其中类名、协议名、结构体、枚举等类型的命名规范通常是,大写字母作为开始,其余单词首字母大写,例如类名HelloWorldApp。

完成controller调控两大板块

我们已经知道了model里面是一个struct,所以我们需要一个变量来装载它(class类似的)。然后我们需要让他时刻更新view。方法和上面很类似,这里把关键函数updateUI贴出来。

 func updateUI(){
        switch FacialExpression.eye{
        case .open:
            faceView.eyeOpen = true
        case .close:
            faceView.eyeOpen = false
        default:
            break
        }

        switch FacialExpression.mouth{
        case .smile:
            faceView.smileFactor = 1.0
        case .relax:
            faceView.smileFactor = 0.0
        case .sad:
            faceView.smileFactor = -1.0
        default:
            break
        }

        switch FacialExpression.eyebrow{
        case .happy:
            faceView.eyeBrowTiltFactor = 1.0
        case .relax:
            faceView.eyeBrowTiltFactor = 0.0
        case .sad:
            faceView.eyeBrowTiltFactor = -1.0
        default:
            break
        }
    }

手势

手势的基类是UIGestureReconizer,但是这是一个抽象的概念,真正的类是各种手势的GestureReconizer( 比如pinch和swipe)。而且必须View才能使用它们。一般来说是controller添加到view。不过一些基本的gesture可能本事就在view上。当识别成功,会使用handler来传输信息,如果不涉及和model沟通,那么只需要在view中,否则需要在controller中设立。
这里写图片描述
这里写图片描述
这里写图片描述

接下来尝试使用SwipeGestureRecognizer来改变Model的状况(请看下图对比改变Model和只是改变View的区别)。
这里写图片描述

let happyReconizer = UISwipeGestureRecognizer(target: self, action: #selector(FaceViewController.happier))
happyReconizer.direction = .down           faceView.addGestureRecognizer(happyReconizer)

分别是创建reconizer、设置属性、以及添加进去view里面。
我们来看UIGestureReconizer的基本格式(target:self/view, action: #selector(controller/view.函数名))。
所有的对应的处理函数func前面都要加入@objc。

@objc func happier(){
    FacialExpression.mouth = FacialExpression.mouth.moreSmile()
    }           

同样的reconizer也可以通过在storyboard拖拽加入然后使用ctrl在controller中建立联系。

@IBAction func eyeChange(_ recognizer: UITapGestureRecognizer) {
        if recognizer.state == .ended{
            switch FacialExpression.eye{
            case .open:
                FacialExpression.eye = .close
            case .close:
                FacialExpression.eye = .open
            default:
                break
            }
        }
    }

多重MVC

这里写图片描述
这里我们使用xcode提供给我们的MVC,常用的分别有以下三种。
第一种是TabBar。
这里写图片描述
第二种是Split,常用在ipad。左边一般是master,右边叫做detail。potrait的时候一般master隐藏在左边,可以抽屉式抽取出来。
这里写图片描述
第三种是NavigationControl。
这里写图片描述
需要知道的是,ios在处理多级菜单的时候,是使用stack来存的。每一个页面push进去,然后返回就是pop。这一切是通过一个更高一级的controller(比如NavigationControl)来控制的。

在这个root controller,里面有一个变量(array)来记录旗下的所有MVC。
var viewControllers: [UIViewController]? {get set}
这里写图片描述

上一篇:final、finally和finalize的区别 下一篇:一步步打造自己的通用上拉加载布局