自定义标签栏,中央按钮将被按下的索引隐藏

Custom Tab Bar, with central button which will be hide by pressed index

我如何创建带有 4 个项目和一个位于中心的 FAB 的自定义选项卡栏,但 Fab 按钮仅在我按下索引 3 时显示,对于索引 0 .. 3 fab.isHidden = true。如果移动,我不需要那个按钮的动画,但填充是加号),只显示/隐藏。为了创建自定义标签蝙蝠,我使用本指南https://medium.com/better-programming/draw-a-custom-ios-tabbar-shape-27d298a7f4fa, I am need create tab bar like this , I am try maenter image description here任何其他方式但无法解决问题。

Custom tab bar which needed

我正在尝试这种方式

import UIKit

@IBDesignable
class CustomizedTabBar: UITabBar {

   
    private var shapeLayer: CALayer?

    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.lineWidth = 1.0

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

        self.shapeLayer = shapeLayer
    }

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

    func createPath() -> CGPath {

        let height: CGFloat = 37.0
        let path = UIBezierPath()
        let centerWidth = self.frame.width / 2

        path.move(to: CGPoint(x: 0, y: 0)) // start top left
        path.addLine(to: CGPoint(x: (centerWidth - height * 2), y: 0)) // the beginning of the trough

////        // first curve down
//      path.addCurve(to: CGPoint(x: centerWidth, y: height), controlPoint1: CGPoint(x: (centerWidth - 25), y: 0), controlPoint2: CGPoint(x: centerWidth - 35, y: height))
//
////        // second curve up
//        path.addCurve(to: CGPoint(x: (centerWidth + height * 1.0), y: 0),
//                    controlPoint1: CGPoint(x: centerWidth + 25, y: height), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0))
        
        // cirle inside tab bar
        path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: height, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false)
        

        // complete the rect

        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 point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let buttonRadius: CGFloat = 35
        return abs(self.center.x - point.x) > buttonRadius || abs(point.y) > buttonRadius
    }

    func createPathCircle() -> CGPath {

        let radius: CGFloat = 37.0
        let path = UIBezierPath()
        let centerWidth = self.frame.width / 2

        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: (centerWidth - radius * 2), y: 0))
        path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: radius, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false)
        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
    }
}

extension CGFloat {
    var degreesToRadians: CGFloat { return self * .pi / 180 }
    var radiansToDegrees: CGFloat { return self * 180 / .pi }
}

第二种方式

import UIKit

class MainTabBar: UITabBar {

    var checkState: Bool = true
    
   public var middleButton = UIButton()

    override func awakeFromNib() {
        super.awakeFromNib()
        
        guard let tabItems = items else { return }
        tabItems[1].titlePositionAdjustment = UIOffset(horizontal: -10, vertical: 0)
        tabItems[2].titlePositionAdjustment = UIOffset(horizontal: 10, vertical: 0)

 setupMiddleButton()
        
   
  
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if self.isHidden {
            return super.hitTest(point, with: event)
        }
        
        let from = point
        let to = middleButton.center
        

        return sqrt((from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y)) <= 39 ? middleButton : super.hitTest(point, with: event)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        middleButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: 5)
    }

    func setupMiddleButton() {

        middleButton.frame.size = CGSize(width: 70, height: 70)
        middleButton.layer.cornerRadius = 35
        middleButton.layer.masksToBounds = true
        middleButton.layer.borderWidth = 8
        middleButton.layer.borderColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
        middleButton.backgroundColor = #colorLiteral(red: 0.2447069331, green: 0.850134835, blue: 0.1531122658, alpha: 1)
        middleButton.setImage(UIImage(named: "plus.png"), for: .normal)
        middleButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: 0)
        middleButton.addTarget(self, action: #selector(test), for: .touchUpInside)
       
        addSubview(middleButton)
        
    }

    @objc func test() {
        print("my name is jeff")
    }
    
      

}

我正在尝试文章中的这种方式 https://equaleyes.com/blog/2017/09/04/the-common-raised-center-button-problems-in-tabbar/
但这对我不起作用,而且我从 WWW 上阅读了太多信息,但没有得到答案(

我创建了这个标签栏,我们需要几个步骤。 创建 ViewController 并嵌入“TabBarController”,然后需要首先为“UITabBar”创建两个 class 这个 class 包含形状和你想要的“UITabBar”,第二个 class对于“UITabBarController”在 ViewControllers 之间切换,我们可以添加动画....这是需要的,因为我的 TabBar 有 4 个选项卡,只有在最后一个选项卡上我有带动画的中央 FAB 按钮,我应该制作动画出现按钮时我的 2 和 3 ui 标签栏元素的位置。

Class 对于“UITabBar”

import UIKit

@IBDesignable
class CustomizedTabBar: UITabBar {
    
    // MARK:- Variables -
    @objc public var centerButtonActionHandler: ()-> () = {}

    @IBInspectable public var centerButton: UIButton?
    @IBInspectable public var centerButtonColor: UIColor?
    @IBInspectable public var centerButtonHeight: CGFloat = 50.0
    @IBInspectable public var padding: CGFloat = 5.0
    @IBInspectable public var buttonImage: UIImage?
    @IBInspectable public var buttonTitle: String?
    
    @IBInspectable public var tabbarColor: UIColor = UIColor.lightGray
    @IBInspectable public var unselectedItemColor: UIColor = .init(red: 0.58, green: 0.61, blue: 0.66, alpha: 1.0)
    @IBInspectable public var selectedItemColor: UIColor = UIColor.black
    
    public var arc: Bool = true {
        didSet {
            self.setNeedsDisplay()
        }
    }
    
    private var shapeLayer: CALayer?

    private func addShape() {
        
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        
        
        shapeLayer.strokeColor = UIColor.white.cgColor
        shapeLayer.fillColor = #colorLiteral(red: 0.96, green: 0.96, blue: 0.96, alpha: 1)
        shapeLayer.lineWidth = 1.0

        if let oldShapeLayer = self.shapeLayer {
            self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
        } else {
            self.layer.insertSublayer(shapeLayer, at: 0)
        }
        
        
        self.shapeLayer = shapeLayer
        self.tintColor = centerButtonColor
        self.unselectedItemTintColor = unselectedItemColor
        self.tintColor = selectedItemColor
        self.setupMiddleButton()
        
    }

    override func draw(_ rect: CGRect) {
        self.addShape()
    }
    
    override public 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
    }

    func createPath() -> CGPath {

        let padding: CGFloat = 5.0
        let centerButtonHeight: CGFloat = 53.0
        
        let f = CGFloat(centerButtonHeight / 2.0) + padding
        let h = frame.height
        let w = frame.width
        let halfW = frame.width/2.0
        let r = CGFloat(18)
        let path = UIBezierPath()
        path.move(to: .zero)
        
        if (!arc) {
            
            path.addLine(to: CGPoint(x: halfW-f-(r/2.0), y: 0))

            path.addQuadCurve(to: CGPoint(x: halfW-f, y: (r/2.0)), controlPoint: CGPoint(x: halfW-f, y: 0))

            path.addArc(withCenter: CGPoint(x: halfW, y: (r/2.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false)

            path.addQuadCurve(to: CGPoint(x: halfW+f+(r/2.0), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0))
        }
        path.addLine(to: CGPoint(x: w, y: 0))
        path.addLine(to: CGPoint(x: w, y: h))
        path.addLine(to: CGPoint(x: 0.0, y: h))
        path.close()

        return path.cgPath
    }
    
    private func setupMiddleButton() {
        
        centerButton = UIButton(frame: CGRect(x: (self.bounds.width / 2)-(centerButtonHeight/2), y: -16, width: centerButtonHeight, height: centerButtonHeight))
        centerButton!.setNeedsDisplay()
        centerButton!.layer.cornerRadius = centerButton!.frame.size.width / 2.0
        centerButton!.setTitle(buttonTitle, for: .normal)
        centerButton!.setImage(UIImage(named: "plus"), for: .normal)
        centerButton!.backgroundColor = .init(red: 0.07, green: 0.83, blue: 0.05, alpha: 1.0)
        centerButton!.tintColor = UIColor.white

        self.centerButton!.isHidden = true

        if (!self.arc) {

            DispatchQueue.main.async {
                
                UIView.transition(with: self.centerButton!, duration: 1,
                    options: .transitionCrossDissolve,
                    animations: {

                        self.centerButton!.isHidden = false

                })
            }
        }
        
        
        //add to the tabbar and add click event
        self.addSubview(centerButton!)
        centerButton!.addTarget(self, action: #selector(self.centerButtonAction), for: .touchUpInside)
    }


    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let buttonRadius: CGFloat = 35
        return abs(self.center.x - point.x) > buttonRadius || abs(point.y) > buttonRadius
    }

    func createPathCircle() -> CGPath {

        let radius: CGFloat = 37.0
        let path = UIBezierPath()
        let centerWidth = self.frame.width / 2

        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: (centerWidth - radius * 2), y: 0))
        path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: radius, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false)
        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
    }
    
    // Menu Button Touch Action
    @objc func centerButtonAction(sender: UIButton) {
       self.centerButtonActionHandler()
    }
    
}

extension CGFloat {
    var degreesToRadians: CGFloat { return self * .pi / 180 }
    var radiansToDegrees: CGFloat { return self * 180 / .pi }
}

和 class 用于 UITabBarController

导入 UIKit

class MyTabBarController: UITabBarController {
    override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
        let myTabBar = tabBar as! CustomizedTabBar
        if (myTabBar.items?[3] == item) {
            myTabBar.arc = false
        } else {
            myTabBar.arc = true
        }
    }
}