(作业)View,Layer,Animation和Gesture

又开始做作业了,这次虽然设计的知识点比较多,但是都比较简单,一个页面就能完成。先来看看要求吧
这里写图片描述

根据这个要求,我们先要自定制一个视图,所以需要新建一个继承自UIView的类,这里博主就使用MyView来命名。
然后在这个类中,我们需要先重写它的draw(_:)方法。

override func draw(_ rect: CGRect) {
        let viewRect = UIBezierPath(rect: rect)

        let redColor = CGFloat(Float(arc4random() % 255) / 255)
        let greenColor = CGFloat(Float(arc4random() % 255) / 255)
        let blueColor = CGFloat(Float(arc4random() % 255) / 255)

        UIColor(red: redColor, green: greenColor, blue: blueColor, alpha: 1.0).set()

        viewRect.stroke()
        viewRect.fill()
    }

在这个代码中,我们使用arc4random()方法来生成一个随机数,这个方法会返回一个UInt32的随机数,然后使用这个方法来生成rgb颜色,使用该颜色填充和描边矩形区域。

然后我们需要重写一个初始化函数

override init(frame: CGRect) {
        super.init(frame: frame)

        setup()
    }

因为如果子类自己重写了初始化函数的话,也必须实现UIView的另一个初始化函数,但我们需要在初始化函数中对自定义的视图类加一些功能,所以我们就把这些添加的功能放在一个setup()方法当中了。必须实现的初始化方法如下:

required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        setup()
    }

在这个setup()方法中,我们可以设置视图的阴影并添加要求的手势

func setup() {
        //设置矩形区域圆角
        self.layer.cornerRadius = 20
        //设置阴影
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowOffset = CGSize(width: 5, height: 5)
        self.layer.shadowOpacity = 0.8
        //设置视图的内容模式为重绘
        self.contentMode = .redraw

        //pan移动
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(pan(gesture:)))
        self.addGestureRecognizer(panGesture)

        //tap删除
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tap(gesture:)))
        self.addGestureRecognizer(tapGesture)

        //pinch缩放
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinch(gesture:)))
        self.addGestureRecognizer(pinchGesture)

        //rotation旋转
        let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotation(gestrue:)))
        self.addGestureRecognizer(rotationGesture)
    }

其中设置视图内容模式为重绘的好处是本次作业要求视图的大小和方向可以改变,当设置为重绘后,在每次改变时,他会将视图重新绘制,这样就不会出现锯齿或毛边,但这样会牺牲一些显卡和内存的资源。
在将视图设置完成之后就可以向视图添加手势了。添加手势都是用UIGestrueRecognizer的子类。用法如上,在初始化手势时,需要传入一个手势对应的方法,我们分别将对应的方法列举如下:

//pan移动
    @objc func pan(gesture: UIPanGestureRecognizer) {
        self.center = gesture.location(in: superview)
    }

    //tap删除
    @objc func tap(gesture: UITapGestureRecognizer) {
        self.removeFromSuperview()
    }

    //pinch缩放
    @objc func pinch(gesture: UIPinchGestureRecognizer) {
        let scale = gesture.scale
        self.transform = self.transform.scaledBy(x: scale, y: scale)
        gesture.scale = 1
    }

    //rotation旋转
    @objc func rotation(gestrue: UIRotationGestureRecognizer) {
        let rotation = gestrue.rotation
        self.transform = self.transform.rotated(by: rotation)
        gestrue.rotation = 0
    }

在pinch手势和rotation手势当中,我们在使用完手势获取的值之后,都需要将它初始化一次,否则的话手势的值会在之前的基础上增加,比如pinch手势,你第一次放大了2倍,第二次想再放大2倍,你所预想的结果是最后的视图的大小是原始的4倍,但如果你不初始化的话,最后的结果会是2+4=6倍。rotation手势类似。

到这里,我们自定义视图需要实现的方法就已经完成了,接下来该在控制器当中来使用我们自定义的视图了。首先为了方便管理按钮,我们将UIWindow的根视图设置为导航栏控制器(下面这句代码添加到AppDelegate.swift当中)

self.window?.rootViewController = UINavigationController(rootViewController: ViewController())

然后我们在控制器的viewDidLoad()方法中,设置根视图的标题和背景颜色,然后想导航栏上添加三个按钮

title = "Custom View"
        self.view.backgroundColor = UIColor.white

        let addBtn = UIBarButtonItem(title: "添加", style: .plain, target: self, action: #selector(addView))
        let moveBtn = UIBarButtonItem(title: "移动", style: .plain, target: self, action: #selector(moveViews))
        self.navigationItem.rightBarButtonItems = [addBtn, moveBtn]

        let clearBtn = UIBarButtonItem(title: "清空", style: .plain, target: self, action: #selector(clearView))
        self.navigationItem.leftBarButtonItem = clearBtn

我们先来实现“添加”按钮的功能,当点击这个按钮后,我们要在随机位置生成随机大小的自定义视图,所以我们需要随机生成一个点,这个点就是自定义视图的左上角的点,然后再随机生成大小值,我们就用这个点和大小来初始化一个自定义视图,并添加到根视图上即可。

@objc func addView() {
        let maxWidth: CGFloat = 150

        let x = CGFloat(arc4random() % UInt32(self.view.bounds.width))
        let y = CGFloat(arc4random() % UInt32(self.view.bounds.height - 40)) + 40
        let point = CGPoint(x: x, y: y)

        let width = CGFloat(arc4random() % UInt32(maxWidth))
        let height = CGFloat(arc4random() % UInt32(maxWidth))
        let size = CGSize(width: width, height: height)

        let view = MyView(frame: CGRect(origin: point, size: size))
        self.view.addSubview(view)
    }

(博主为了不是随机生成的自定义视图太大,所以设置了其最大宽度)

然后实现“清除”按钮,用户点击这个按钮之后,会将控制器根视图上的所有子视图全部移除。所以要用的subViews方法返回根视图的子视图的数组,然后博主使用数组的map方法来移除视图,读者如果不熟悉map方法,也可以使用循环完成。

@objc func clearView() {
        self.view.subviews.map { $0.removeFromSuperview() }
    }

最后就是随机移动视图了。在iOS 4.0之前做动画的步骤非常繁琐,但在iOS 4.0之后,Apple公司提供了一个UIView 的工厂方法来实现一些简单的动画,我们这里使用这个就可以了。

@objc func moveViews() {
        self.view.subviews.map { (view) in
            let x = CGFloat(arc4random() % UInt32(self.view.bounds.width))
            let y = CGFloat(arc4random() % UInt32(self.view.bounds.height - 40)) + 40
            let point = CGPoint(x: x, y: y)

            UIView.animate(withDuration: 3, animations: {
                view.center = point
            })
        }
    }

这个方法的使用也很简单,我们只需要先随机生成一个点,然后调用UIView 的animate(withDuration:animations:)方法就可以实现了,在这个方法中,只需要填入动画结束之后的效果即可,可以改变视图的位置、大小、方向,甚至我们可以使用一些特殊的动画,比如淡入淡出等等,读者可以自行查找相关文章阅读。(如果读者们想省事的话,可以留言博主发学习笔记给你们,如果人数太多,博主也可以写一篇相关的博文)

到这里本次的作业就完成了。
下面看看运行的效果吧:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为下列代码实现可暂停效果: import UIKit class ViewController: UIViewController { private let radarAnimation = "radarAnimation" private var animationLayer: CALayer? private var animationGroup: CAAnimationGroup? private var opBtn: UIButton! override func viewDidLoad() { super.viewDidLoad() let first = makeRadarAnimation(showRect: CGRect(x: 120, y: 100, width: 100, height: 100), isRound: true) view.layer.addSublayer(first) opBtn = UIButton(frame: CGRect(x: 100, y: 450, width: 80, height: 80)) opBtn.backgroundColor = UIColor.red opBtn.clipsToBounds = true opBtn.setTitle("Hsu", for: .normal) opBtn.layer.cornerRadius = 10 view.addSubview(opBtn) let second = makeRadarAnimation(showRect: opBtn.frame, isRound: false) view.layer.insertSublayer(second, below: opBtn.layer) } @IBAction func startAction(_ sender: UIButton) { animationLayer?.add(animationGroup!, forKey: radarAnimation) } @IBAction func stopAction(_ sender: UIButton) { animationLayer?.removeAnimation(forKey: radarAnimation) } private func makeRadarAnimation(showRect: CGRect, isRound: Bool) -> CALayer { // 1. 一个动态波 let shapeLayer = CAShapeLayer() shapeLayer.frame = showRect // showRect 最大内切圆 if isRound { shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: showRect.width, height: showRect.height)).cgPath } else { // 矩形 shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: showRect.width, height: showRect.height), cornerRadius: 10).cgPath } shapeLayer.fillColor = UIColor.orange.cgColor // 默认初始颜色透明度 shapeLayer.opacity = 0.0 animationLayer = shapeLayer // 2. 需要重复的动态波,即创建副本 let replicator = CAReplicatorLayer() replicator.frame = shapeLayer.bounds replicator.instanceCount = 4 replicator.instanceDelay = 1.0 replicator.addSublayer(shapeLayer) // 3. 创建动画组 let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.fromValue = NSNumber(floatLiteral: 1.0) // 开始透明度 opacityAnimation.toValue = NSNumber(floatLiteral: 0) // 结束时透明底 let scaleAnimation = CABasicAnimation(keyPath: "transform") if isRound { scaleAnimation.fromValue = NSValue.init(caTransform3D: CATransform3DScale(CATransform3DIdentity, 0, 0, 0)) // 缩放起始大小 } else { scaleAnimation.fromValue = NSValue.init(caTransform3D: CATransform3DScale(CATransform3DIdentity, 1.0, 1.0, 0)) // 缩放起始大小 } scaleAnimation.toValue = NSValue.init(caTransform3D: CATransform3DScale(CATransform3DIdentity, 1.5, 1.5, 0)) // 缩放结束大小 let animationGroup = CAAnimationGroup() animationGroup.animations = [opacityAnimation, scaleAnimation] animationGroup.duration = 3.0 // 动画执行时间 animationGroup.repeatCount = HUGE // 最大重复 animationGroup.autoreverses = false self.animationGroup = animationGroup shapeLayer.add(animationGroup, forKey: radarAnimation) return replicator } }
最新发布
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值