【问题标题】:Blur a custom TabBar in UIKit在 UIKit 中模糊自定义 TabBar
【发布时间】:2021-03-15 07:16:20
【问题描述】:

使用iOS14.2、Swift5.3.1、Xcode 12.2,

按照this link,我实现了一个具有新形状的自定义TabBar。

形状由 CAShapeLayer 及其对应的 BezierPath 给出(参见下面的代码示例)。

但是,我想模糊这个自定义 TabBar,保持 BezierPath 形状作为它的遮罩,同时保持模糊透明。

请参阅最底部的附加视频,以说明我的意思。

这是自定义 TabBar 形状的代码(这部分效果很好)

import UIKit

@IBDesignable
class CustomTabBar: UITabBar {

    private var shapeLayer: CALayer?
    var scanTabShown = true

    override func draw(_ rect: CGRect) {
        self.addShape()
    }

    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = self.createPath()
        shapeLayer.strokeColor = UIColor.lightGray.cgColor
        shapeLayer.fillColor = #colorLiteral(red: 0.9782002568, green: 0.9782230258, blue: 0.9782107472, alpha: 0.75)
        shapeLayer.lineWidth = 0.5
        shapeLayer.shadowOffset = CGSize(width:0, height:0)
        shapeLayer.shadowRadius = 10
        shapeLayer.shadowColor = UIColor.gray.cgColor
        shapeLayer.shadowOpacity = 0.3

        if let oldShapeLayer = self.shapeLayer {
            self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
        } else {
            self.layer.insertSublayer(shapeLayer, at: 0)
        }
        self.shapeLayer = shapeLayer
    }

    func createPath() -> CGPath {
        let height: CGFloat = 72.0
        let path = UIBezierPath()
        let centerWidth = self.frame.width / 2
        if scanTabShown {
            path.move(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: (centerWidth - 31 ), y: 0))
            path.addCurve(to: CGPoint(x: centerWidth, y: height - 40),
                          controlPoint1: CGPoint(x: (centerWidth - 30), y: 0), controlPoint2: CGPoint(x: centerWidth - 29, y: height - 40))
            path.addCurve(to: CGPoint(x: (centerWidth + 31 ), y: 0),
                          controlPoint1: CGPoint(x: centerWidth + 29, y: height - 40), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
            path.addLine(to: CGPoint(x: 0, y: self.frame.height))
            path.close()
        } else {
            path.move(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
            path.addLine(to: CGPoint(x: 0, y: self.frame.height))
            path.close()
        }

        return path.cgPath
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
        for member in subviews.reversed() {
            let subPoint = member.convert(point, from: self)
            guard let result = member.hitTest(subPoint, with: event) else { continue }
            return result
        }
        return nil
    }
}

extension UITabBar {
    override open func sizeThatFits(_ size: CGSize) -> CGSize {
        var sizeThatFits = super.sizeThatFits(size)
        sizeThatFits.height = 83
        return sizeThatFits
    }
}

为了实现蒙版的BlurView,我关注了this link

这是我的代码,据说应该为这个自定义形状的 TabBar 添加模糊效果。

但是代码仍然不起作用。怎么了?

addShape() 方法中,我添加了以下代码以获得所需的效果。但同样,它不起作用。为什么???

        let maskLayer = CAShapeLayer()
        maskLayer.path = shapeLayer.path
        maskLayer.fillRule = .evenOdd
        
        let maskView = UIView(frame: self.frame)
        maskView.backgroundColor = UIColor.black
        maskView.layer.mask = maskLayer
        
        let blurEffect = UIBlurEffect(style: .regular)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)
        blurEffectView.frame = self.frame
        blurEffectView.alpha = 1.0
        blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        blurEffectView.mask = maskView
        
        self.layer.addSublayer(blurEffectView.layer)

这里有一个插图:

请注意,顶部的导航栏显示了所需的半透明和模糊行为。

底部带有 BezierPath 的 TabBar 是完全白色的。但我也希望它是半透明和模糊的......我怎样才能做到这一点?

我已经尝试使用shapeLayer.fillColor 的 alpha 值。半透明正在工作。但不是模糊!!!

另一个尝试是将自定义形状的 tabBar 模糊为 tabBarController 中的 subView。但同样,没有模糊!

这里是试用的代码:

class SessionTabBarController: UITabBarController, UITabBarControllerDelegate {

override func viewDidLoad() {
        super.viewDidLoad()
        
        if let tabBar = self.tabBar as? CustomTabBar {
            let maskLayer = CAShapeLayer()
            maskLayer.path = tabBar.createPath()
            maskLayer.fillRule = .evenOdd
    
            let maskView = UIView(frame: tabBar.frame)
            maskView.backgroundColor = UIColor.black
            maskView.layer.mask = maskLayer
    
            let blurEffect = UIBlurEffect(style: .regular)
            let blurEffectView = UIVisualEffectView(effect: blurEffect)
            blurEffectView.backgroundColor = .green
            blurEffectView.tintColor = .yellow
            blurEffectView.frame = tabBar.frame
            blurEffectView.alpha = 1.0
            blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            blurEffectView.mask = maskView
            tabBar.addSubview(blurEffectView)
            self.view.bringSubviewToFront(tabBar)
        }
    }
}

【问题讨论】:

  • 您能否提供一张图片说明您正在努力实现的目标以及您已经设法实现的目标?
  • @Sti:我在原始问题中添加了一个短视频。请不要顶部导航栏的半透明和模糊。但是,底部的 TabBar 不会显示相同的效果。如何实现自定义绘制的 TabBar 的模糊?
  • Hm.. 也许尝试将 UIVisualEffectView 作为子视图添加到您的自定义选项卡视图,并将遮罩/形状应用到该视图?
  • 不幸的是,您将其作为自定义选项卡视图的子视图添加的建议也不起作用!请参阅解释我尝试过的原始帖子中的附加代码。还有其他方法可以模糊自定义形状的 tabBar 吗?

标签: swift uikit uitabbarcontroller draw cashapelayer


【解决方案1】:

我终于找到了解决办法:

@Sti 的回答让我找到了正确的方向。

Trick 是将自定义形状的 blurView 单独添加到自定义形状的 tabBar 中。

我在viewWillLayoutSubviews方法里面做如下:

override func viewWillLayoutSubviews() {
    
    if let tabBar = self.tabBar as? CustomTabBar {
        
        let maskLayer = CAShapeLayer()
        maskLayer.path = tabBar.createPath()
        maskLayer.fillRule = .evenOdd

        let blurEffect = UIBlurEffect(style: .light)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)
        blurEffectView.frame = self.tabBar.frame
        blurEffectView.layer.mask = maskLayer
        
        self.view.addSubview(blurEffectView)
        self.view.bringSubviewToFront(tabBar)
    }
}

自定义形状的 CustomTabBar-extension 保持不变:

import UIKit

@IBDesignable
class CustomTabBar: UITabBar {

    private var shapeLayer: CALayer?
    var scanTabShown = true

    override func draw(_ rect: CGRect) {
        self.addShape()
    }

    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = self.createPath()
        shapeLayer.strokeColor = UIColor.lightGray.cgColor
        shapeLayer.fillColor = #colorLiteral(red: 0.9782002568, green: 0.9782230258, blue: 0.9782107472, alpha: 0.75)
        shapeLayer.lineWidth = 0.5
        shapeLayer.shadowOffset = CGSize(width:0, height:0)
        shapeLayer.shadowRadius = 10
        shapeLayer.shadowColor = UIColor.gray.cgColor
        shapeLayer.shadowOpacity = 0.3

        if let oldShapeLayer = self.shapeLayer {
            self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
        } else {
            self.layer.insertSublayer(shapeLayer, at: 0)
        }
        self.shapeLayer = shapeLayer
    }

    func createPath() -> CGPath {
        let height: CGFloat = 72.0
        let path = UIBezierPath()
        let centerWidth = self.frame.width / 2
        if scanTabShown {
            path.move(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: (centerWidth - 31 ), y: 0))
            path.addCurve(to: CGPoint(x: centerWidth, y: height - 40),
                          controlPoint1: CGPoint(x: (centerWidth - 30), y: 0), controlPoint2: CGPoint(x: centerWidth - 29, y: height - 40))
            path.addCurve(to: CGPoint(x: (centerWidth + 31 ), y: 0),
                          controlPoint1: CGPoint(x: centerWidth + 29, y: height - 40), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
            path.addLine(to: CGPoint(x: 0, y: self.frame.height))
            path.close()
        } else {
            path.move(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: 0))
            path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
            path.addLine(to: CGPoint(x: 0, y: self.frame.height))
            path.close()
        }

        return path.cgPath
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
        for member in subviews.reversed() {
            let subPoint = member.convert(point, from: self)
            guard let result = member.hitTest(subPoint, with: event) else { continue }
            return result
        }
        return nil
    }
}

extension UITabBar {
    override open func sizeThatFits(_ size: CGSize) -> CGSize {
        var sizeThatFits = super.sizeThatFits(size)
        sizeThatFits.height = 83
        return sizeThatFits
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    • 2021-08-13
    • 2021-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多