简体   繁体   中英

Mask UIView with another UIView

Yes this question has been asked before, the solutions did not work or had different applications.

It is the most basic setup. I have two rectangular UIViews, red and blue. I would like the blue square to cut into the red square, so the red square looks like an "L"

在此输入图像描述

import Foundation
import UIKit

class TestController: UIViewController {
    override func viewDidLoad() {
        view.backgroundColor = .gray
        view.addSubview(viewA)
        view.addSubview(maskView)

        viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
        viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
        viewA.translatesAutoresizingMaskIntoConstraints = false

        maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
        maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
        maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
        maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        maskView.translatesAutoresizingMaskIntoConstraints = false

        // Things which don't work
        //viewA.mask = maskView // both views disappear
        //viewA.layer.mask = maskView.layer // both views disappear
        //viewA.layer.addSublayer(maskView.layer) // hides mask view
    }

    var viewA: UIView = {
        let view = UIView()
        view.backgroundColor = .red
        view.layer.masksToBounds = true
        return view
    }()

    var maskView: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        return view
    }()
}

This is the result I am expecting: (done in Photoshop)

在此输入图像描述

As there is no magic way to mask the way in iOS, I present here a simple way to achieve this.

Don't forget to pan the clear area, If leaving the red square, it will become a blue square.

It's not hard to modify the subclass of UIViews for your own purpose, especially views.

    import UIKit

        class TestController: UIViewController {

                override func viewDidLoad() {
                    view.backgroundColor = .gray
                    view.addSubview(viewA)
                    view.addSubview(maskView)
                    maskView.maskedView = viewA
                    viewA.activeMask = maskView
                    viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
                    viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
                    viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
                    viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
                    viewA.translatesAutoresizingMaskIntoConstraints = false
                    maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
                    maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
                    maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
                    maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
                    maskView.translatesAutoresizingMaskIntoConstraints = false
                }

                var viewA: MyUIView = {
                    let view = MyUIView()
                    view.backgroundColor = .clear
                    view.layer.masksToBounds = true
                    return view
                }()


            var maskView: ActiveMaskView = {
                let view = ActiveMaskView()
                    view.backgroundColor = .clear
                    return view
                }()


            }

        class ActiveMaskView: UIView{
            override func didMoveToSuperview() {
                super.didMoveToSuperview()
                let panGesture =  UIPanGestureRecognizer.init(target: self, action: #selector(moveAround(_:)))
                self.addGestureRecognizer(panGesture)
            }
              weak  var maskedView : UIView?
            private var frameOrigin : CGPoint = CGPoint.zero

          @objc  func moveAround(_ panGesture: UIPanGestureRecognizer){
            guard let superview = superview else {return}
                switch panGesture.state {
                case .began:
                  frameOrigin = frame.origin
                    self.backgroundColor = UIColor.blue
                case .changed:
                    let translation  = panGesture.translation(in: superview)
                    frame = CGRect.init(origin: CGPoint.init(x: frameOrigin.x + translation.x, y: frameOrigin.y + translation.y), size: frame.size)
                    maskedView?.setNeedsDisplay()
                    break
                case .ended:
                    self.backgroundColor =
                        frame.intersects(maskedView!.frame) ?
                    UIColor.clear : UIColor.blue
                    maskedView?.setNeedsDisplay()
                case .cancelled:
                    frame = CGRect.init(origin: frameOrigin , size: frame.size)
                    self.backgroundColor =
                        frame.intersects(maskedView!.frame) ?
                    UIColor.clear : UIColor.blue
                    maskedView?.setNeedsDisplay()
                default:
                    break;
                }
            }
        }

        class MyUIView: UIView{

          weak  var activeMask: ActiveMaskView?

            override func draw(_ rect: CGRect) {
                super.draw(rect)
                let ctx = UIGraphicsGetCurrentContext()
                ctx?.setFillColor(UIColor.red.cgColor)
                ctx?.fill(self.layer.bounds)
                ctx?.setBlendMode(.sourceOut)
                guard let activeMask = activeMask , let superview = superview else {
                   return
                }
              let sc = frame.intersection(activeMask.frame)
            let interSection = superview.convert(sc, to: self)
                ctx?.fill(interSection )
             }
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM