All existed solutions are not perfect.
Since UIVisualEffectView implements its visual effects by compositing two subviews contents - one is a backdrop view and the other one is a filter view, when you implementing your corner radius by masking the whole UIVisualEffectView, there would be some dirty colors around corners.

To get rid of these dirty colors, you just need to mask UIVisualEffects's filter view.
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()
}
}
And all things done!
