【问题标题】:Rotating UIControl with CAGradientLayer not updating correctly Swift使用 CAGradientLayer 旋转 UIControl 不能正确更新 Swift
【发布时间】:2019-04-10 10:04:01
【问题描述】:

我没有使用普通按钮,而是将UIControl 子类化,因为我需要向它添加渐变。我还有一种方法可以添加阴影和活动指示器(在下图中不可见)作为有状态按钮,以在(例如)进行 API 调用时阻止用户敲击按钮。

尝试让UIControl 旋转真的很棘手,为了能够做到这一点,我将阴影作为单独的视图添加到包含UIControl 的容器视图中,因此可以添加阴影。

现在的问题是控件的行为不像旋转视图 - 让我向您展示上下文的屏幕抓取:

这是中间旋转,但肉眼几乎可以看到 - 图像显示渐变是图像中蓝色UIView 长度的 75%。

https://github.com/stevencurtis/statefulbutton

为了执行这种旋转,我删除了shadowview,然后将渐变框架的框架更改为其边界,这就是问题所在。

func viewRotated() {
    CATransaction.setDisableActions(true)

    shadowView!.removeFromSuperview()
    shadowView!.frame = self.frame
    shadowView!.layer.masksToBounds = false
    shadowView!.layer.shadowOffset = CGSize(width: 0, height: 3)
    shadowView!.layer.shadowRadius = 3
    shadowView!.layer.shadowOpacity = 0.3
    shadowView!.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 20, height: 20)).cgPath
    shadowView!.layer.shouldRasterize = true
    shadowView!.layer.rasterizationScale = UIScreen.main.scale

    self.gradientViewLayer.frame = self.bounds
    self.selectedViewLayer.frame = self.bounds

    CATransaction.commit()

    self.insertSubview(shadowView!, at: 0)

}

所以这个旋转方法是通过父视图控制器调用的:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { context in
        context.viewController(forKey: UITransitionContextViewControllerKey.from)
        //inform the loginButton that it is being rotated
        self.loginButton.viewRotated()
    }, completion: { context in
        //  can call here when completed the transition
    })
}

我知道这就是问题所在,而且我想这不是在完全正确的时间以与UIView 相同的方式发生。现在的问题是,我已经尝试了很多方法来让它发挥作用,但我最好的解决方案(上图)并不完全存在。

建议使用UIButton,将图像用于渐变(请不要建议使用渐变图像作为 UIButton 的背景,我已经尝试过)或第三个党图书馆。这是我的工作,它可以工作,但对我来说不能接受,我想让它像通常的视图一样工作(或者至少知道为什么不工作)。我也尝试了上面的其他解决方案,并选择了我自己的UIControl。我知道如果有 API 调用,我可以锁定视图,或者使用其他方式来阻止用户多次按下按钮。我正在尝试修复我的解决方案,而不是通过CAGradientLayer 发明解决此问题的方法。

问题:我需要将UIControlViewCAGradientLayer 作为背景以与UIView 相同的方式旋转,并且不会出现上图所示的问题.

完整示例: https://github.com/stevencurtis/statefulbutton

【问题讨论】:

  • 你继承 UIControl 的理由有点奇怪。
  • 你能向 UIButton 添加渐变吗?有例子吗?
  • 是的。您可以将渐变图层添加到任何具有图层的视图,包括按钮。
  • 您还可以为 UIButtons 添加阴影,您可以启用/禁用它们以阻止用户向它们发送垃圾邮件。

标签: swift cglayer


【解决方案1】:

这是工作代码:

https://gist.github.com/alldne/22d340b36613ae5870b3472fa1c64654

这些是我对您的代码的建议:

1。设置子层大小和位置的适当位置

视图的大小,即您的按钮,是在布局完成后确定的。您应该做的只是在布局后设置适当的子层大小。所以我建议你在layoutSubviews中设置渐变子层的大小和位置。

    override func layoutSubviews() {
        super.layoutSubviews()

        let center = CGPoint(x: self.bounds.width / 2, y: self.bounds.height / 2)
        selectedViewLayer.bounds = self.bounds
        selectedViewLayer.position = center
        gradientViewLayer.bounds = self.bounds
        gradientViewLayer.position = center
    }

2。你不需要使用额外的视图来绘制阴影

删除shadowView,只设置图层属性:

layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowRadius = 3
layer.shadowOpacity = 0.3
layer.shadowColor = UIColor.black.cgColor
clipsToBounds = false

如果您必须使用额外的视图来绘制阴影,那么您可以在init() 中添加一次视图并在layoutSubviews 中设置适当的大小和位置,或者您可以通过编程方式为超级视图设置自动布局约束。

3。动画时长和定时功能

设置合适的尺寸后,渐变层的动画和容器视图不能很好地同步。

看来:

  • 在旋转过渡期间,coordinator(UIViewControllerTransitionCoordinator) 有自己的过渡持续时间和缓动函数。
  • 并且持续时间和缓动功能会自动应用于所有子视图 (UIView)。
  • 但是,如果没有关联的UIView,这些值不会应用于CALayer。因此,它使用 CoreAnimation 的默认计时函数和持续时间。

要同步动画,请显式设置动画持续时间和计时函数,如下所示:

class ViewController: UIViewController {
    ...

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        CATransaction.setAnimationDuration(coordinator.transitionDuration)
        CATransaction.setAnimationTimingFunction(coordinator.completionCurve.timingFunction)
    }

    ...
}

// Swift 4
extension UIView.AnimationCurve {
    var timingFunction: CAMediaTimingFunction {
        let functionName: CAMediaTimingFunctionName
        switch self {
        case .easeIn:
            functionName = kCAMediaTimingFunctionEaseIn as CAMediaTimingFunctionName
        case .easeInOut:
            functionName = kCAMediaTimingFunctionEaseInEaseOut as CAMediaTimingFunctionName
        case .easeOut:
            functionName = kCAMediaTimingFunctionEaseOut as CAMediaTimingFunctionName
        case .linear:
            functionName = kCAMediaTimingFunctionLinear as CAMediaTimingFunctionName
        }
        return CAMediaTimingFunction(name: functionName as String)
    }
}

【讨论】:

    猜你喜欢
    • 2014-10-23
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2017-12-03
    • 1970-01-01
    • 2013-07-07
    相关资源
    最近更新 更多