【问题标题】:Swift ios cut out rounded rect from view allowing colour changesSwift ios 从视图中切出圆角矩形,允许颜色变化
【发布时间】:2019-07-02 16:28:31
【问题描述】:

我正在使用这种方法从背景视图中切出一个圆角矩形“窗口”:

override func draw(_ rect: CGRect) {
        guard let rectsArray = rectsArray else {
            return
        }

        for holeRect in rectsArray {
            let holeRectIntersection = rect.intersection(holeRect)

            if let context = UIGraphicsGetCurrentContext() {
                let roundedWindow = UIBezierPath(roundedRect: holeRect, cornerRadius: 15.0)
                if holeRectIntersection.intersects(rect) {
                    context.addPath(roundedWindow.cgPath)
                    context.clip()
                    context.clear(holeRectIntersection)
                    context.setFillColor(UIColor.clear.cgColor)
                    context.fill(holeRectIntersection)
                }
            }

        }
    }

layoutSubviews()我更新背景颜色添加我的“窗口框架”矩形:

override func layoutSubviews() {
        super.layoutSubviews()
        backgroundColor = self.baseMoodColour
        isOpaque = false
        self.rectsArray?.removeAll()
        self.rectsArray = [dragAreaView.frame]
}

我在这里添加矩形是因为layoutSubviews() 更新了“窗口框架”的大小(即,矩形在layoutSubviews() 运行后发生变化)。

基本机制按预期工作,但是,如果我更改背景颜色,剪切窗口将填充黑色。所以我想知道如何使用这种设置为背景颜色变化设置动画?也就是说,我想为剪切窗口外部区域的颜色设置动画(窗口保持清晰)。

我尝试直接更新backgroundColor,并在我的 UIView 子类的自定义颜色变量的访问器中使用didSet,但两者都会导致“窗口”的填充相同。

    var baseMoodColour: UIColor {
        didSet {
            self.backgroundColor = baseMoodColour
            self.setNeedsDisplay()
        }
    }

【问题讨论】:

  • 抱歉,我没有看到任何“更改背景颜色”或“动画”代码。请说明你在说什么。
  • 好的,我可以稍后更新我的问题,但我实际上只是在谈论在代码中的其他位置调用self.backgroundColor = someNewColour。我有一个我调用的颜色变量,在 didSet 中我将它分配给视图的背景颜色。
  • 但是self.backgroundColor = someNewColour 不是动画。动画部分是什么?
  • 没关系。我可以将颜色变化放在动画块中,但颜色仍然会破坏切口。当我有机会时,我会改写问题以使动画可选。更改背景颜色会破坏剪切“窗口”。这是主要问题。
  • 不确定投反对票是否会影响其他用户找到此解决方案,但如果是,则不应再投反对票。我理解为什么它最初被否决,但问题已得到澄清并提供了解决方案。

标签: ios swift animation uikit


【解决方案1】:

尝试使用UIView.animate,可以查看here

UIView.animate(withDuration: 1.0, delay: 0.0, options: [.curveEaseOut], animations: {
  self.backgroundColor = someNewColour
  //Generally 
  //myView.backgroundColor = someNewColor
}, nil)

【讨论】:

    【解决方案2】:

    短期内的问题是,如果背景颜色不透明,这就是clear 所做的事情。只要给你的背景颜色一些透明度——哪怕是一点点透明度,小到人眼都无法察觉——现在clear 会在视图中切出一个洞。

    例如,如果您将视图的背景颜色设置为UIColor.green.withAlphaComponent(0.99),您的代码就可以正常工作。

    顺便说一句,你应该删除关于UIColor.clear的行;这是一个红鲱鱼。你也应该删掉关于backgroundColor的行;您不应该将背景颜色重新绘制到您的上下文中。它们是两种不同的东西。


    从长远来看,问题在于您所做的不是如何在视图中打孔。您应该改用口罩。这是在保持孔的同时获得动画的唯一方法。

    【讨论】:

    • 澄清一下;注释掉draw(rect:) 的前两行确实没有任何改变,所以这些都消失了。但是,context.setFillColor(UIColor.clear.cgColor) 似乎确实对于实现所需结果至关重要(即,通过该视图中的“窗口”切出另一个视图)。
    • 我确实需要一个“洞”,因为在这个后面还有另一个视图需要显示出来——这个视图有点像相框或窗框围绕另一个。但我很好奇你的说法:“这是你在保持洞的同时获得动画的唯一方法。”你到底是什么意思?是否无法使用我使用的方法为颜色设置动画(我有点怀疑不是,这就是我在此处发布的原因)。如果确实不可能,那该怎么做呢?我的第一次尝试实际上是使用mask,但是当我尝试时,我无法以这种方式获得切口。
    • "但是,context.setFillColor(UIColor.clear.cgColor) 似乎对于实现所需结果是必不可少的(即,通过在此视图中切出的“窗口”显示另一个视图) " 好吧,我可以证明它不是。
    • “我的第一次尝试实际上是使用面具,但是当我尝试时,我无法以这种方式获得切口。”好吧,回去再试一次,因为这正是你可以用面具做的事情。例如见我的stackoverflow.com/a/23452614/341994
    • 优秀。感谢您的链接,我稍后会检查它
    【解决方案3】:

    根据@matt 的建议(和链接的示例)回答我自己的问题,我使用 CAShapeLayer 进行了回答。我的要求中有一个额外的“障碍”,因为我在需要屏蔽的视图之上还有几个视图。所以,我做了这样的掩蔽:

    func cutOutWindow() {
            // maskedBackgroundView is an additional view, inserted ONLY for the mask
            let r = self.maskedBackgroundView.bounds
            // Adjust frame for dragAreaView's border
            var dragSize = self.dragAreaView.frame.size
            var dragPosition = self.dragAreaView.frame.origin
            dragSize.width -= 6.0
            dragSize.height -= 6.0
            dragPosition.x += 3.0
            dragPosition.y += 3.0
            let r2 = CGRect(x: dragPosition.x, y: dragPosition.y, width: dragSize.width, height: dragSize.height)
            let roundedWindow = UIBezierPath(roundedRect: r2, cornerRadius: 15.0)
            let mask = CAShapeLayer()
            let path = CGMutablePath()
            path.addPath(roundedWindow.cgPath)
            path.addRect(r)
            mask.path = path
            mask.fillRule = kCAFillRuleEvenOdd
            self.maskedBackgroundView.layer.mask = mask
        }
    

    然后我必须将颜色更改应用于maskedBackgroundView.layer.backgroundColor(即应用于图层,而不是视图)。有了它,我就得到了我需要的剪裁,并带有可动画的颜色变化。感谢@matt 为我指明了正确的方向。

    【讨论】:

      猜你喜欢
      • 2013-11-03
      • 2010-12-26
      • 1970-01-01
      • 1970-01-01
      • 2013-11-23
      • 1970-01-01
      • 1970-01-01
      • 2011-12-30
      • 1970-01-01
      相关资源
      最近更新 更多