标签栏视图和底部弹出窗口中的曲线不起作用?

curve in tabbar view and bottom popup not working?

Hello all, I tried to add arc for UIBezierPath I could not able to get the exact curve,

这里是我的代码,我已经为从中心位置添加的曲线添加了贝塞尔曲线路径。

@IBDesignable
class MyTabBar: UITabBar {
    private var shapeLayer: CALayer?
    private func addShape() {
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = createPath()
        shapeLayer.strokeColor = UIColor.lightGray.cgColor
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.lineWidth = 1.0
        
        //The below 4 lines are for shadow above the bar. you can skip them if you do not want a shadow
        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
    }
    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
        
        path.addCurve(to: CGPoint(x: centerWidth, y: height),
                      controlPoint1: CGPoint(x: (centerWidth - 30), y: 0), controlPoint2: CGPoint(x: centerWidth - 35, y: height))
        
        path.addCurve(to: CGPoint(x: (centerWidth + height * 2), y: 0),
                      controlPoint1: CGPoint(x: centerWidth + 35, y: height), 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()
        
        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
    }
}

这是在中心视图中心添加加号按钮的标签栏控制器,当点击加号按钮添加基于曲线的弹出窗口时应该显示,我不知道如何添加基于曲线的弹出窗口。

class TabbarViewController: UITabBarController,UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        self.navigationController?.navigationBar.isHidden = true
        setupMiddleButton()
    }
    // TabBarButton – Setup Middle Button
    func setupMiddleButton() {
        let middleBtn = UIButton(frame: CGRect(x: (self.view.bounds.width / 2)-25, y: -20, width: 50, height: 50))
        middleBtn.setImage(UIImage(named: "PlusBtn"), for: .normal)
        self.tabBar.addSubview(middleBtn)
        middleBtn.addTarget(self, action: #selector(self.menuButtonAction), for: .touchUpInside)
        self.view.layoutIfNeeded()
    }
    // Menu Button Touch Action
    @objc func menuButtonAction(sender: UIButton) {
        //show the popUp
    }
}

请分享我的发现并分享你的评论 谢谢

新编辑:

我创建了一个 general-purpose 方法,该方法将生成混合了不同半径的尖角和圆角的多边形。我用它来创建一个看起来很像你所追求的项目。您可以从 Github:

下载

https://github.com/DuncanMC/CustomTabBarController.git

这是它的样子:

请注意,选项卡视图控制器中延伸到选项卡栏控制器中的区域不会被点击。如果您尝试点击那里,它会触发标签栏控制器。您将不得不做更多的修补才能使该部分正常工作。

最终,您可能必须创建一个自定义 UITabBar(或 UITabBar-like 组件),并且可能还需要创建一个像 UITabBar 一样的自定义父视图控制器,才能获得您想要的内容。

创建多边形路径的方法叫做buildPolygonPathFrom(points:defaultCornerRadius:)

它接受一个 PolygonPoint 结构数组。这些定义如下:

/// A struct describing a single vertex in a polygon. Used in building polygon paths with a mixture of rounded an sharp-edged vertexes.
public struct PolygonPoint {
    let point: CGPoint
    let isRounded: Bool
    let customCornerRadius: CGFloat?
    init(point: CGPoint, isRounded: Bool, customCornerRadius: CGFloat? = nil) {
        self.point = point
        self.isRounded = isRounded
        self.customCornerRadius = customCornerRadius
    }

    init(previousPoint: PolygonPoint, isRounded: Bool) {
        self.init(point: previousPoint.point, isRounded: isRounded, customCornerRadius: previousPoint.customCornerRadius)
    }
}

构建自定义标签栏路径的代码如下所示:

func tabBarMaskPath() -> CGPath? {
    let width = bounds.width
    let height = bounds.height
    guard width > 0 && height > 0 else { return nil }
    let dentRadius: CGFloat = 35
    let cornerRadius: CGFloat = 20
    let topFlatPartWidth = (width - dentRadius * 2.0) / 2
    let polygonPoints = [
        PolygonPoint(point: CGPoint(x:  0, y:  0),                                                          // Point 0
                     isRounded: true,
                     customCornerRadius: cornerRadius),
        PolygonPoint(point: CGPoint(x:  0, y:  height),                                                     // Point 1
                     isRounded: false),
        PolygonPoint(point: CGPoint(x:  width, y:  height),                                                 // Point 2
                     isRounded: false),
        PolygonPoint(point: CGPoint(x:  width, y:  0),                                                      // Point 3
                     isRounded: true,
                     customCornerRadius: cornerRadius),
        PolygonPoint(point: CGPoint(x:  topFlatPartWidth + dentRadius * 2, y:  0),                          // Point 4
                     isRounded: true,
                     customCornerRadius: cornerRadius),
        PolygonPoint(point: CGPoint(x:  topFlatPartWidth + dentRadius * 2, y:  dentRadius + cornerRadius),  // Point 5
                     isRounded: true,
                     customCornerRadius: dentRadius),
        PolygonPoint(point: CGPoint(x:  topFlatPartWidth , y:  dentRadius  + cornerRadius),                  // Point 6
                     isRounded: true,
                     customCornerRadius: dentRadius),
        PolygonPoint(point: CGPoint(x:  topFlatPartWidth , y:  0),                                           // Point 7
                     isRounded: true,
                     customCornerRadius: cornerRadius),
    ]
    return buildPolygonPathFrom(points: polygonPoints, defaultCornerRadius: 0)
}

上一个回答:

我刚刚试过了,可以继承UITabBar。我创建了 UITabBar 的一个子类,我在其中使用遮罩层从选项卡栏的顶部切出一个圆形“槽口”。代码如下。它看起来像下面的屏幕截图。这不是您想要的,但这是一个开始:

(“Page 1”视图控制器的背景颜色设置为浅灰色,您可以在我从标签栏中切出的“缺口”中看到这种颜色。)

//
//  CustomTabBar.swift
//  TabBarController
//
//  Created by Duncan Champney on 3/31/21.
//

import UIKit

class CustomTabBar: UITabBar {

    var maskLayer = CAShapeLayer()

    override var frame: CGRect {
        didSet {
            configureMaskLayer()
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        configureMaskLayer()
        self.layer.mask = maskLayer
        self.layer.borderWidth = 0
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        configureMaskLayer()
        self.layer.mask = maskLayer
    }

    func     configureMaskLayer() {
        let rect = layer.bounds
        maskLayer.frame = rect
        let circleBoxSize = rect.size.height * 1.25
        maskLayer.fillRule = .evenOdd
        let path = UIBezierPath(rect: rect)
        let circleRect = CGRect(x: rect.size.width/2 - circleBoxSize / 2,
                                y: -circleBoxSize/2,
                                width: circleBoxSize,
                                height: circleBoxSize)
        let circle = UIBezierPath.init(ovalIn: circleRect)
        path.append(circle)
        maskLayer.path = path.cgPath
        maskLayer.fillColor = UIColor.white.cgColor // Any opaque color works and has no effect
    }
}

编辑:

要绘制弹出视图,您需要创建一个该形状的填充路径。您必须使用直线和圆弧的组合来构建自定义形状。我建议使用 CGMutablePath 和方法 addArc(tangent1End:tangent2End:radius:transform:),因为这使您能够提供端点而不是角度。

编辑#2:

谜题的另一部分:

这是一个自定义的 UIView 子类,它以您想要的形状隐藏自己

//
//  ShapeWithTabView.swift
//  ShapeWithTab
//
//  Created by Duncan Champney on 4/1/21.
//

import UIKit

class ShapeWithTabView: UIView {

    var cornerRadius: CGFloat = 20
    var tabRadius: CGFloat = 60
    var tabExtent: CGFloat = 0

    var shapeLayer = CAShapeLayer()
    var maskLayer = CAShapeLayer()

    func buildShapeLayerPath() -> CGPath {
        let boxWidth = min(bounds.size.width - 40, 686)
        let boxHeight = min(bounds.size.height - 40 - tabRadius * 2 - tabExtent, 832)

        // These are the corners of the view's primary rectangle
        let point1 = CGPoint(x: 0, y: boxHeight)
        let point2 = CGPoint(x: 0, y: 0)
        let point3 = CGPoint(x: boxWidth, y: 0)
        let point4 = CGPoint(x: boxWidth, y: boxHeight)

        // These are the corners of the "tab" that extends outside the view's normal bounds.
        let tabPoint1 = CGPoint(x: boxWidth / 2 + tabRadius, y: boxHeight)
        let tabPoint2 = CGPoint(x: boxWidth / 2 + tabRadius, y: boxHeight + tabExtent + tabRadius * 2 )
        let tabPoint3 = CGPoint(x: boxWidth / 2 - tabRadius, y: boxHeight + tabExtent + tabRadius * 2)
        let tabPoint4 = CGPoint(x: boxWidth / 2 - tabRadius , y: boxHeight)

        let path = CGMutablePath()
        path.move(to: CGPoint(x: 0, y: boxHeight - cornerRadius))
        path.addArc(tangent1End: point2,
                    tangent2End: point3,
                    radius: cornerRadius)
        path.addArc(tangent1End: point3,
                    tangent2End: point4,
                    radius: cornerRadius)
        path.addArc(tangent1End: point4,
                    tangent2End: point1,
                    radius: cornerRadius)
//
        path.addArc(tangent1End: tabPoint1,
                    tangent2End: tabPoint2,
                    radius: tabRadius)
        path.addArc(tangent1End: tabPoint2,
                    tangent2End: tabPoint3,
                    radius: tabRadius)
        path.addArc(tangent1End: tabPoint3,
                    tangent2End: tabPoint4,
                    radius: tabRadius)
        path.addArc(tangent1End: tabPoint4,
                    tangent2End: point1,
                    radius: tabRadius)

        path.addArc(tangent1End: point1,
                    tangent2End: point2,
                    radius: cornerRadius)

        return path
    }

    func doInitSetup() {
        self.layer.addSublayer(shapeLayer)
        self.layer.mask = maskLayer
        backgroundColor = .lightGray

        //Configure a shape layer to draw an outline
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineWidth = 2

        //Configure a mask layer to mask the view to our custom shape
        maskLayer.fillColor = UIColor.white.cgColor
        maskLayer.strokeColor = UIColor.white.cgColor
        maskLayer.lineWidth = 2
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.doInitSetup()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.doInitSetup()
    }
    public func updateShapeLayerPath() {
        let path = buildShapeLayerPath()
        shapeLayer.path =  path
        maskLayer.path = path
    }

    override var frame: CGRect {
        didSet {
            print("New frame = \(frame)")
            shapeLayer.frame = layer.bounds
        }
    }
}

结合上面修改后的标签栏,如下图所示。最后的任务是正确调整自定义视图的大小和位置,并将其放置在选项卡栏的顶部。