UIBezierPath和CAShapeLayer创建不规则View(Swift 3.0)

最近一个朋友在做图片处理的 App,想要实现类似 MOLDIV App 拼图的UI效果(如何创建不规则的 view),就问我有什么想法。我首先想到的就是 UIBezierPath+CAShapeLayer的方式,为了验证自己的想法,写了一个小 demo

效果图:


demo.gif

实现思路

正常情况下我们创建的 view 都是矩形的。但是我们知道 view 的内容显示靠的是 layer 层,所以可以通过修改 layer 层来实现。代码如下:

func drawMaskLayer() {
        guard (points != nil) else {
            return
        }
        let bezierPath1 = UIBezierPath()
        bezierPath1.move(to: points![0])
        for index in 1..<points!.count {
            bezierPath1.addLine(to: points![index])
        }
        bezierPath1.addLine(to: points![0])

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = bezierPath1.cgPath
        self.layer.mask = shapeLayer
    }

通过这种方式我们可以创建出任何的多边形的 view。通过这种方式我们可以很容易的创建出上图所示的 UI 布局。但是我们这个时候会发现,当有触摸点在 view 透明的地方时,触摸事件并不能传递给被它遮挡的 view 上,所以需要重写 func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 方法来判断何时响应。代码如下:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if points != nil {
            if (super.hitTest(point, with: event) == scrollV) {
                print(self,point)
                print(points!)
                let isInner = isInnerView(with: point)
                if isInner {
                    return self.scrollV
                }else {
                    return nil
                }
            }

        }else {

                return super.hitTest(point, with: event)

        }
        return nil
    }

那么何时响应,何时不响应呢?应该是如果触摸点在我们绘制的 view 范围内响应,之外的不相应。那么这个时候就需要判断一个点在是否在多边形内,本 demo 使用 射线法

fileprivate func isInnerView(with point: CGPoint) -> Bool {
        let pX = point.x , pY = point.y
        var flag = false

        var oldIndex = points!.count - 1
        for newIndex in 0..<points!.count {
            let newX = points![newIndex].x
            let newY = points![newIndex].y
            let oldX = points![oldIndex].x
            let oldY = points![oldIndex].y

            // 点与多边形顶点重合
            if (pX == newX && pY == newY) || (pX == oldX && pY == oldY) {
                return false
            }

            // 判断线段两端点是否在射线两侧
            if (oldY > pY && newY < pY) || (oldY < pY && newY > pY) {
                // 线段上与射线 Y 坐标相同的点的 X 坐标
                let x = newX + (pY - newY) * (oldX - newX) / (oldY - newY)

                // 点在多边形的边上
                if(x == pX) {
                    return false
                }

                // 射线穿过多边形的边界
                if(x > pX) {
                    flag = !flag
                }
            }
            oldIndex = newIndex
        }
        // 射线穿过多边形边界的次数为奇数时点在多边形内
        return flag ? true : false
    }

到了这里 demo 也就基本完成了。希望对需要的人有帮助,有什么不对的欢迎指正。

源代码:
ZCFJointView
环境:
Xcode 8.2 、Swift 3.0
参考资料:
射线法


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值