触发UITapGestureRecognizer后,NSLayoutConsstraint常量不会影响视图

我有一个视图( self.view )是用另一个视图(而不是图层)使用UIView.mask属性UIView.mask 。 在UIPanGestureRecognizer我安装了一个UIPanGestureRecognizer所以当我在屏幕上平移面具变得越来越小相应。 另外我安装在UITapGestureRecognizer ,它增加了animationUIImageView s到屏幕上,他们在UIBezierPath进行animationUIBezierPath 。 我正在更新面具的大小与约束。

问题是,我点击屏幕添加animation的意见后,我对面具约束的变化停止影响。 我可以在日志中看到确实改变了约束的常量,并且UIPanGestureRecognizer仍然在工作。

所以我的意思是说遮罩视图约束停止影响其视图。 这是为什么? 谢谢

video插图: https : //youtu.be/UtNuc8nicgs

这里是代码:

 class UICircle: UIView { init() { super.init(frame: .zero) self.clipsToBounds = true self.backgroundColor = .yellow self.isUserInteractionEnabled = false } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } var diameterConstraint: NSLayoutConstraint? var animating = false func updateSize(_ delta: CGFloat, animated: Bool = false) { if animating { return } if animated { animating = true diameterConstraint?.constant = UIScreen.main.bounds.height * 2.1 let duration: TimeInterval = 0.6 let animation = CABasicAnimation(keyPath: "cornerRadius") animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) animation.fromValue = self.layer.cornerRadius animation.toValue = UIScreen.main.bounds.height * 2.1 / 2 animation.duration = duration self.layer.add(animation, forKey: nil) UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseOut], animations: { self.superview?.layoutIfNeeded() }, completion: { (success) in if success { self.animating = false } }) } else { let newSize = diameterConstraint!.constant + (delta * 2.85) if newSize > 60 && newSize < UIScreen.main.bounds.height * 2.1 { diameterConstraint?.constant = newSize } } } override func didMoveToSuperview() { super.didMoveToSuperview() if let superv = superview { self.makeSquare() self.centerHorizontallyTo(superv) let c = NSLayoutConstraint.init(item: self, attribute: .centerY, relatedBy: .equal, toItem: superv, attribute: .bottom, multiplier: 1, constant: -40) c.isActive = true diameterConstraint = self.constrainHeight(superv.frame.height * 2.1) } } override func layoutSubviews() { super.layoutSubviews() self.layer.cornerRadius = self.frame.width / 2 } } class ViewController: UIViewController, UIGestureRecognizerDelegate { var circle = UICircle() override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor.init(red: 48/255, green: 242/255, blue: 194/255, alpha: 1) self.view.clipsToBounds = true let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tap.delegate = self self.view.addGestureRecognizer(tap) setupCircle() } func setupCircle() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) panGesture.delegate = self self.view.addGestureRecognizer(panGesture) self.view.mask = circle } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } var panStarted = false func handlePan(_ pan: UIPanGestureRecognizer) { let delta = pan.translation(in: self.view).y if pan.state == .began { if delta > 0 { panStarted = true circle.updateSize(-delta) } } else if pan.state == .changed { if panStarted { circle.updateSize(-delta) } } else if pan.state == .ended || pan.state == .cancelled { if panStarted { circle.updateSize(self.view.frame.height * 2.1, animated: true) } panStarted = false } pan.setTranslation(.zero, in: self.view) } func handleTap() { let num = Int(5 + drand48() * 10) (1 ... num).forEach { (_) in addView() } } override var prefersStatusBarHidden: Bool { get { return true } } func addView() { var image: UIImageView! let dd = drand48() if dd < 0.5 { image = UIImageView(image: #imageLiteral(resourceName: "heart1")) } else { image = UIImageView(image: #imageLiteral(resourceName: "heart2")) } image.isUserInteractionEnabled = false image.contentMode = .scaleAspectFit let dim: CGFloat = 20 + CGFloat(10 * drand48()) image.constrainHeight(dim) image.constrainWidth(dim) let animation = CAKeyframeAnimation(keyPath: "position") let duration = Double(1.5 * self.view.frame.width / CGFloat((60 + drand48() * 40))) // duration = way / speed animation.path = getPath().cgPath animation.duration = duration animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) animation.fillMode = kCAFillModeForwards animation.isRemovedOnCompletion = false image.layer.add(animation, forKey: nil) DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + duration + 1) { DispatchQueue.main.async { image.removeFromSuperview() } } if drand48() < 0.3 { UIView.animate(withDuration: 0.2 + 0.1 * drand48() , delay: TimeInterval(drand48() * 1), options: [.curveEaseOut, .repeat, .autoreverse], animations: { image.transform = CGAffineTransform.init(scaleX: 1.5, y: 1.5) }, completion: nil) } self.view.addSubview(image) } func getPath() -> UIBezierPath { let path = UIBezierPath() let startPoint = CGPoint.init(x: -30, y: self.view.frame.height / 2) path.move(to: startPoint) let r = CGFloat(400 * drand48()) let cp1 = CGPoint.init(x: self.view.frame.width * 0.33, y: self.view.frame.height * 0.25 - r) let cp2 = CGPoint.init(x: self.view.frame.width * 0.66, y: self.view.frame.height * 0.75 + r) let endPoint = CGPoint.init(x: self.view.frame.width + 30, y: self.view.frame.height / 2) path.addCurve(to: endPoint, controlPoint1: cp1, controlPoint2: cp2) return path } } extension UIView { @discardableResult func makeSquare() -> NSLayoutConstraint { self.turnOffMaskResizing() let constraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.height, multiplier: 1.0, constant: 0) NSLayoutConstraint.activate([constraint]) return constraint } @discardableResult func centerHorizontallyTo(_ toItem: UIView, padding: CGFloat) -> NSLayoutConstraint { self.turnOffMaskResizing() let constraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: toItem, attribute: NSLayoutAttribute.centerX, multiplier: 1.0, constant: padding) NSLayoutConstraint.activate([constraint]) return constraint } @discardableResult func constrainHeight(_ height: CGFloat, priority: UILayoutPriority = 1000) -> NSLayoutConstraint { self.turnOffMaskResizing() let constraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.height, multiplier: 0, constant: height) constraint.priority = priority NSLayoutConstraint.activate([constraint]) return constraint } @discardableResult func constrainWidth(_ width: CGFloat) -> [NSLayoutConstraint] { self.turnOffMaskResizing() let constraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[item(width)]", metrics: ["width" : width], views: ["item" : self]) NSLayoutConstraint.activate(constraints) return constraints } func turnOffMaskResizing() { self.translatesAutoresizingMaskIntoConstraints = false } } 

我认为这是因为你添加了新的对象,这将影响约束,他们打破了。 我提出的是将圆圈添加为子视图,因此与其他对象无关。

这是我试过的,它的工作

  override func viewDidLoad() { super.viewDidLoad() let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tap.delegate = self self.view.addGestureRecognizer(tap) setupCircle() } func setupCircle() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) panGesture.delegate = self self.view.addSubview(circle) self.circle.backgroundColor = UIColor.init(red: 48/255, green: 242/255, blue: 194/255, alpha: 1) self.circle.clipsToBounds = true self.view.addGestureRecognizer(panGesture) } 

编辑:添加图像的变化将发生在您的层次结构

在点击之前 在这里输入图像说明

点击后 在这里输入图像说明

你的面具似乎删除后,水龙头 – 但我不知道如何解决这个问题,仍然不明白为什么你不能添加子视图

这是我的概念的certificate,从https://stackoverflow.com/a/33076583/4284508采取和重做CircleMaskView&#x3002; 这就是你所需要的。 这有点混乱,所以不要把它当做一件事。 我使用你的类来获得另一个面具的框架和半径,所以你需要以某种方式摆脱它,并以其他方式计算半径和框架。 但它会起作用

 /// Apply a circle mask on a target view. You can customize radius, color and opacity of the mask. class CircleMaskView { private var fillLayer = CAShapeLayer() var target: UIView? var fillColor: UIColor = UIColor.gray { didSet { self.fillLayer.fillColor = self.fillColor.cgColor } } var radius: CGFloat? { didSet { self.draw() } } var opacity: Float = 0.5 { didSet { self.fillLayer.opacity = self.opacity } } /** Constructor - parameter drawIn: target view - returns: object instance */ init(drawIn: UIView) { self.target = drawIn } /** Draw a circle mask on target view */ func draw() { guard let target = target else { print("target is nil") return } var rad: CGFloat = 0 let size = target.frame.size if let r = self.radius { rad = r } else { rad = min(size.height, size.width) } let path = UIBezierPath(roundedRect: CGRect(x:0, y:0, width:size.width, height:size.height), cornerRadius: 0.0) let circlePath = UIBezierPath(roundedRect: CGRect(x:size.width / 2.0 - rad / 2.0, y:0, width:rad, height:rad), cornerRadius: rad) path.append(circlePath) path.usesEvenOddFillRule = true fillLayer.path = path.cgPath fillLayer.fillRule = kCAFillRuleEvenOdd fillLayer.fillColor = self.fillColor.cgColor fillLayer.opacity = self.opacity target.layer.addSublayer(fillLayer) } func redraw(withCircle circle: UICircle) { guard let target = target else { print("target is nil") return } var rad: CGFloat = 0 let size = target.frame.size if let r = self.radius { rad = r } else { rad = min(size.height, size.width) } let path = UIBezierPath(roundedRect: CGRect(x:0, y:0, width:size.width, height:size.height), cornerRadius: 0.0) let circlePath = UIBezierPath(roundedRect: circle.frame, cornerRadius: circle.diameterConstraint!.constant) path.append(circlePath) path.usesEvenOddFillRule = true fillLayer.path = path.cgPath fillLayer.fillRule = kCAFillRuleEvenOdd fillLayer.fillColor = self.fillColor.cgColor fillLayer.opacity = self.opacity target.layer.sublayers?.forEach { $0.removeFromSuperlayer() } target.layer.addSublayer(fillLayer) } /** Remove circle mask */ func remove() { self.fillLayer.removeFromSuperlayer() } } 

 var circle = UICircle() var circleMask: CircleMaskView? var subviewC = UIView() override func viewDidLoad() { super.viewDidLoad() self.subviewC.clipsToBounds = true let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tap.delegate = self self.view.addGestureRecognizer(tap) view.backgroundColor = UIColor.init(red: 48/255, green: 242/255, blue: 194/255, alpha: 1) subviewC.backgroundColor = .clear subviewC.frame = view.frame self.view.addSubview(subviewC) self.view.addSubview(circle) circle.backgroundColor = .clear setupCircle() } func setupCircle() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) panGesture.delegate = self self.subviewC.addGestureRecognizer(panGesture) circleMask = CircleMaskView(drawIn: subviewC) circleMask?.opacity = 1.0 circleMask?.draw() } override func viewDidLayoutSubviews() { circleMask?.redraw(withCircle: circle) } 

 func handlePan(_ pan: UIPanGestureRecognizer) { let delta = pan.translation(in: self.view).y if pan.state == .began { if delta > 0 { panStarted = true circle.updateSize(-delta) circleMask?.redraw(withCircle: circle) } } else if pan.state == .changed { if panStarted { circle.updateSize(-delta) circleMask?.redraw(withCircle: circle) } } else if pan.state == .ended || pan.state == .cancelled { if panStarted { circle.updateSize(self.view.frame.height * 2.1, animated: true) circleMask?.redraw(withCircle: circle) } panStarted = false } pan.setTranslation(.zero, in: self.view) }