【问题标题】:Add subview on a parent drawn through UIBezierPath在通过 UIBezierPath 绘制的父视图上添加子视图
【发布时间】:2019-11-30 08:51:27
【问题描述】:

我有一个自定义的 UITabBar。它的栏有一个简单但自定义的形状:它的高度比默认的大,有圆角和(重要的)顶部的阴影层。 结果是这样的:

现在我要添加一个元素,在栏的顶部显示所选部分,以实现此目的:

问题是无论我选择添加这个元素的方式(添加一个子视图到栏或添加一个新的子层)但是新元素总是会被绘制在角落之外。我想这是因为我无法启用剪贴蒙版(如果启用剪贴蒙版,我将杀死阴影,更重要的是,贝塞尔路径)

您对此有什么建议吗? 基本上,目标应该是:

有一个元素可以水平移动(动画)但不能在父级(标签栏)之外绘制

其实绘制自定义tabBar的代码是:

class CustomTabBar: UITabBar {

    /// The layer that defines the custom shape
    private var shapeLayer: CALayer?
    /// The radius for the border of the bar
    var borderRadius: CGFloat = 0

    override func layoutSubviews() {
        super.layoutSubviews()

        // aspect and shadow
        isTranslucent       = false
        backgroundColor     = UIColor.white
        tintColor           = AZTheme.PaletteColor.primaryColor
        shadowImage         = nil
        layer.masksToBounds = false
        layer.shadowColor   = UIColor.black.cgColor
        layer.shadowOpacity = 0.1
        layer.shadowOffset  = CGSize(width: 0, height: -1)
        layer.shadowRadius  = 10
    }

    override func draw(_ rect: CGRect) {
        drawShape()
    }

    /// Draw and apply the custom shape to the bar
    func drawShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        shapeLayer.fillColor = AZTheme.tabBarControllerBackgroundColor.cgColor

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

        self.shapeLayer = shapeLayer
    }
}

// MARK: - Private functions
extension CustomTabBar {
    /// Return the custom shape for the bar
    internal func createPath() -> CGPath {
        let height: CGFloat = self.frame.height
        let path = UIBezierPath()

        path.move(to: CGPoint(x: 0, y: 0))
        path.addArc(withCenter: CGPoint(x: borderRadius, y: 0), radius: borderRadius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * (3/2), clockwise: true)
        path.addLine(to: CGPoint(x: frame.width - borderRadius, y: -borderRadius))
        path.addArc(withCenter: CGPoint(x: frame.width - borderRadius, y: 0), radius: borderRadius, startAngle: CGFloat.pi * (3/2), endAngle: 0, clockwise: true)
        path.addLine(to: CGPoint(x: frame.width, y: height))
        path.addLine(to: CGPoint(x: 0, y: height))
        path.close()

        return path.cgPath
    }
}

【问题讨论】:

    标签: ios swift calayer mask uibezierpath


    【解决方案1】:

    我解决了在 2 个不同的视图中将自定义形状的所有者与阴影的所有者分开的问题。所以我使用 3 个视图来实现目标。

    CustomTabBar: has default size and casts shadow with offset.
    |
    └ SelectorContainer: is a view with custom shape (BezierPath) that is
      positioned on the top of the TabBar to graphically "extend" the view
      and have the feeling of a bigger TabBar. It has rounded corners on
      the top-right, top-left margin. MaskToBounds enabled.
      |
      └ Selector: simple view that change the its origin through animation.
    

    See the result here

    代码:

    class CustomTabBar: UITabBar {
    
        /// The corner radius value for the top-left, top-right corners of the TabBar
        var borderRadius: CGFloat = 0
    
        /// Who is containing the selector. Is a subview of the TabBar.
        private var selectorParent: UIView?
        /// Who moves itself following the current section. Is a subview of ```selectorParent```.
        private var selector: UIView?
        /// The height of the ```selector```
        private var selectorHeight: CGFloat = 5
        /// The number of sections handled by the TabBarController.
        private var numberOfSections: Int = 0
    
        override func layoutSubviews() {
            super.layoutSubviews()
            isTranslucent       = false
            backgroundColor     = UIColor.white
            tintColor           = AZTheme.PaletteColor.primaryColor
            shadowImage         = nil
            layer.masksToBounds = false
            layer.shadowColor   = UIColor.black.cgColor
            layer.shadowOpacity = 0.1
            layer.shadowOffset  = CGSize(width: 0, height: -1)
            layer.shadowRadius  = 10
        }
    }
    
    // MARK: - Private functions
    extension CustomTabBar {
        /// Create the selector element on the top of the TabBar
        func setupSelectorView(numberOfSections: Int) {
            self.numberOfSections = numberOfSections
    
            // delete previous subviews (if exist)
            if let selectorContainer = self.selectorParent {
                selectorContainer.removeFromSuperview()
                self.selector?.removeFromSuperview()
                self.selectorParent = nil
                self.selector = nil
            }
    
            // SELECTOR CONTAINER
            let selectorContainerRect: CGRect = CGRect(x: 0,
                                                       y: -borderRadius,
                                                       width: frame.width,
                                                       height: borderRadius)
            let selectorContainer = UIView(frame: selectorContainerRect)
            selectorContainer.backgroundColor = UIColor.white
            selectorContainer.AZ_roundCorners([.topLeft, .topRight], radius: borderRadius)
            selectorContainer.layer.masksToBounds = true
            self.addSubview(selectorContainer)
    
            // SELECTOR
            let selectorRect: CGRect = CGRect(x: 0,
                                              y: 0,
                                              width: selectorContainer.frame.width / CGFloat(numberOfSections),
                                              height: selectorHeight)
            let selector = UIView(frame: selectorRect)
            selector.backgroundColor = AZTheme.PaletteColor.primaryColor
            selectorContainer.addSubview(selector)
    
            // add views to hierarchy
            self.selectorParent = selectorContainer
            self.selector = selector
        }
    
        /// Animate the position of the selector passing the index of the new section
        func animateSelectorTo(sectionIndex: Int) {
            guard let selectorContainer = self.selectorParent, let selector = self.selector else { return }
            selector.layer.removeAllAnimations()
    
            let sectionWidth: CGFloat = selectorContainer.frame.width / CGFloat(numberOfSections)
            let newCoord = CGPoint(x: sectionWidth * CGFloat(sectionIndex), y: selector.frame.origin.y)
            UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveEaseOut, animations: { [weak self] in
                self?.selector?.frame.origin = newCoord
            }, completion: nil)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多