【问题标题】:Corner radius on UIVisualEffectViewUIVisualEffectView 上的圆角半径
【发布时间】:2015-05-15 18:20:16
【问题描述】:

您好我想知道是否可以在 UIVisualEffectView 上设置角半径? 这是我尝试过的代码:

@IBOutlet var blurView: UIVisualEffectView!

var blurLayer : CALayer{
    return blurView.layer
}
override func viewDidLoad() {
    super.viewDidLoad()
    setUpLayer()
    // Do any additional setup after loading the view.
}

func setUpLayer(){
    blurLayer.cornerRadius = 50
}

@IBOutlet var blurView: UIVisualEffectView!

override func viewDidLoad() {
    super.viewDidLoad()
    blurView.layer.cornerRadius = 50
    // Do any additional setup after loading the view.
}

它们都不起作用。

【问题讨论】:

  • 你试过'blurView.clipsToBounds = true'吗?
  • 感谢工作就像一个魅力!
  • @MikeM 也许您应该将其放入答案中?

标签: swift xcode6 cornerradius uivisualeffectview


【解决方案1】:

在故事板中添加两个参数在用户定义的运行时属性layer.cornerRadius = 8layer.masksToBounds = true

或在代码中

@IBOutlet var blurView: UIVisualEffectView! {
    didSet {
        blurView.layer.cornerRadius = 8
        blurView.layer.masksToBounds = true
    }
}

【讨论】:

  • 你应该edit这个答案并修正格式。很难分清什么是解释,什么是代码。
【解决方案2】:

所有现有的解决方案都不完美。

由于UIVisualEffectView 通过合成两个子视图内容来实现其视觉效果——一个是背景视图,另一个是过滤视图,当您通过遮盖整个UIVisualEffectView 来实现圆角半径时,会有些脏角落周围的颜色。

要摆脱这些脏颜色,你只需要屏蔽 UIVisualEffects 的过滤视图。

private typealias ObjcRawUIVisualEffectViewSelCGRect =
    @convention(c) (UIVisualEffectView, Selector, CGRect) -> Void

private var cornerRadiusKey =
"com.WeZZard.Waxing.UIVisualEffectView-CornerRadius.cornerRadius"

private var needsUpdateMaskLayerKey =
"com.WeZZard.Waxing.UIVisualEffectView-CornerRadius.needsUpdateMaskLayer"

extension UIVisualEffectView {
    public var cornerRadius: CGFloat {
        get {
            if let storedValue = objc_getAssociatedObject(self,
                &cornerRadiusKey)
                as? CGFloat
            {
                return storedValue
            }
            return 0
        }
        set {
            if cornerRadius != newValue {
                objc_setAssociatedObject(self,
                    &cornerRadiusKey,
                    newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                setNeedsUpdateMaskLayer()
            }
        }
    }

    private var needsUpdateMaskLayer: Bool {
        get {
            if let storedValue = objc_getAssociatedObject(self,
                &needsUpdateMaskLayerKey)
                as? Bool
            {
                return storedValue
            }
            return false
        }
        set {
            objc_setAssociatedObject(self,
                &needsUpdateMaskLayerKey,
                newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }


    public override class func initialize() {
        swizzle_setBounds()
    }

    private class func swizzle_setBounds() {
        struct Static {
            static var token: dispatch_once_t = 0
        }

        dispatch_once(&Static.token) {
            let selector: Selector = "setBounds:"

            let method = class_getInstanceMethod(self, selector)

            let imp_original = method_getImplementation(method)

            before_setBounds = unsafeBitCast(imp_original,
                ObjcRawUIVisualEffectViewSelCGRect.self)

            class_replaceMethod(self,
                selector,
                unsafeBitCast(after_setBounds, IMP.self),
                "@:{_struct=CGRect}")
        }
    }

    private func setNeedsUpdateMaskLayer() {
        needsUpdateMaskLayer = true
        NSOperationQueue.mainQueue().addOperationWithBlock { [weak self] _ in
            self?.updateMaskLayerIfNeeded()
        }
    }

    private func updateMaskLayerIfNeeded() {
        if needsUpdateMaskLayer {
            updateMaskLayer()
            needsUpdateMaskLayer = false
        }
    }

    private func updateMaskLayer(){
        var filterViewFound = false
        for each in subviews {
            if each.dynamicType.description()
                .containsString("Filter")
            {
                filterViewFound = true
                let newPath = UIBezierPath(roundedRect: each.bounds,
                    cornerRadius: self.cornerRadius)
                    .CGPath
                if let existedMask = each.layer.mask
                    as? CAShapeLayer
                {
                    existedMask.path = newPath
                } else {
                    let shapeLayer = CAShapeLayer()
                    shapeLayer.path = newPath
                    each.layer.mask = shapeLayer
                }
            } else {
                setNeedsUpdateMaskLayer()
            }
        }
        assert(filterViewFound == true, "Filter view was not found! Check your hacking!")
    }
}

private var before_setBounds: ObjcRawUIVisualEffectViewSelCGRect = { _ in
    fatalError("No implementation found")
}

private let after_setBounds: ObjcRawUIVisualEffectViewSelCGRect = {
    (aSelf, selector, bounds) -> Void in

    let oldBounds = aSelf.bounds

    before_setBounds(aSelf, selector, bounds)

    if oldBounds.size != bounds.size {
        aSelf.setNeedsUpdateMaskLayer()
    }
}

所有事情都完成了!

【讨论】:

    【解决方案3】:

    在@theMonster 建议之后,我发布了评论。

    override func viewDidLoad() {
        super.viewDidLoad()
        blurView.layer.cornerRadius = 50
        blurView.clipsToBounds = true
    }
    

    【讨论】:

      【解决方案4】:

      子类 UIVisualEffectView

      class PSORoundedVisualEffectView : UIVisualEffectView{
      
          override func layoutSubviews() {
              super.layoutSubviews()
              updateMaskLayer()
          }
      
          func updateMaskLayer(){
              let shapeLayer = CAShapeLayer()
              shapeLayer.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 10).CGPath
              self.layer.mask = shapeLayer
          }
      }
      

      用你想要的任何形状替换 UIBezierPath。

      【讨论】:

      • 谢谢!这真的很好用。但是做一个扩展会更容易。
      • 不是真的,这样更容易