如何在swift ios中添加下边框?

pokxtpni  于 2023-02-17  发布在  iOS
关注(0)|答案(1)|浏览(326)

我正在尝试添加一个底部边框到我的标签。以下是代码:

import UIKit

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.backgroundColor = .systemBackground
    
    self.navigationItem.titleView = UIImageView(
      image: UIImage(named: "Twitter")!.resize(25, 25)
    )
    
    let leftBarImage = UIImageView(
      image: UIImage(named: "Profile")!.resize(35, 35)
    )
    leftBarImage.layer.borderWidth = 1
    leftBarImage.layer.borderColor = UIColor.systemGray4.cgColor
    leftBarImage.layer.cornerRadius = leftBarImage.frame.width / 2
    leftBarImage.clipsToBounds = true
    
    self.navigationItem.leftBarButtonItem = UIBarButtonItem(
      customView: leftBarImage
    )
    
    let segmentedControl = UIStackView()
    segmentedControl.translatesAutoresizingMaskIntoConstraints = false
    segmentedControl.axis = .horizontal
    segmentedControl.alignment = .center
    segmentedControl.distribution = .fillEqually
    segmentedControl.backgroundColor = .green
    segmentedControl.addBorder(
      for: .Bottom,
      color: UIColor.red.cgColor,
      thickness: 20
    )
      
    for menuLabelText in ["For you", "Following"] {
      let menuLabel = UILabel()
      menuLabel.text = menuLabelText
      menuLabel.textAlignment = .center
      menuLabel.font = UIFont.boldSystemFont(ofSize: 16)
      
      segmentedControl.addArrangedSubview(menuLabel)
    }
    
    self.view.addSubview(segmentedControl)
    
    NSLayoutConstraint.activate([
      segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
      segmentedControl.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor)
    ])
  }
}

以及边界逻辑,

import UIKit

extension UIView {
    enum BorderSide {
        case Left, Right, Top, Bottom
    }

    func addBorder(for side: BorderSide, color: CGColor, thickness: CGFloat) {
        let border = CALayer()
        border.backgroundColor = color

        switch side {
          case .Left:
            border.frame = CGRect(
              x: frame.minX,
              y: frame.minY,
              width: thickness,
              height: frame.height
            )
            break
          case .Right:
            border.frame = CGRect(
              x: frame.maxX,
              y: frame.minY,
              width: thickness,
              height: frame.height
            )
            break
          case .Top:
            border.frame = CGRect(
              x: frame.minX,
              y: frame.minY,
              width: frame.width,
              height: thickness
            )
            break
          case .Bottom:
            border.frame = CGRect(
              x: frame.minX,
              y: frame.maxY,
              width: frame.width,
              height: thickness
            )
            break
        }

        layer.addSublayer(border)
    }
}

边框根本不显示。下面是截图:

你可以看到绿色的背景,但没有红色的边界下面。任何帮助将不胜感激。谢谢!

kqlmhetl

kqlmhetl1#

首先,我们来看看代码有什么问题......
UIView扩展中的addBorder(...)函数使用视图的frame--所以,让我们在尝试添加边框之前放置一个print()语句,以查看视图的框架:

segmentedControl.backgroundColor = .green
    
    // print the frame of segmentedControl to debug console
    print("segmentedControl Frame in viewDidLoad():", segmentedControl.frame)
    
    segmentedControl.addBorder(
        for: .Bottom,
        color: UIColor.red.cgColor,
        thickness: 20
    )

您将在调试控制台中看到以下内容:

segmentedControl Frame in viewDidLoad(): (0.0, 0.0, 0.0, 0.0)

因此,您的扩展尝试设置层的帧:

border.frame = CGRect(
    x: frame.minX,       // minX == 0
    y: frame.maxY,       // maxY == 0
    width: frame.width,  // width == 0
    height: thickness.   // thickness == 20 (passed in call)
)

正如我们所看到的,我们最终得到了一个(x: 0.0, y: 0.0, width: 0.0, height: 20.0)的层帧......我们什么也看不到,因为它没有宽度。
因此,让我们尝试在viewDidLayoutSubviews()中添加边框。
注意,我们将把segmentedControl的创建移到viewDidLoad()之外,这样我们就可以在其他地方引用它,并且,我们将把addBorder()保留为原来的red,然后在viewDidLayoutSubviews()中用blue再次调用它:

class ViewController: UIViewController {

    // create it here, so we can reference it outside of viewDidLoad()
    let segmentedControl = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.backgroundColor = .systemBackground
        
        self.title = "Bad Layout"
        
        segmentedControl.translatesAutoresizingMaskIntoConstraints = false
        segmentedControl.axis = .horizontal
        segmentedControl.alignment = .center
        segmentedControl.distribution = .fillEqually
        segmentedControl.backgroundColor = .green
        
        // print the frame of segmentedControl to debug console
        print("segmentedControl Frame in viewDidLoad():", segmentedControl.frame)
        
        segmentedControl.addBorder(
            for: .Bottom,
            color: UIColor.red.cgColor,
            thickness: 20
        )
        
        for menuLabelText in ["For you", "Following"] {
            let menuLabel = UILabel()
            menuLabel.text = menuLabelText
            menuLabel.textAlignment = .center
            menuLabel.font = UIFont.boldSystemFont(ofSize: 16)
            
            segmentedControl.addArrangedSubview(menuLabel)
        }
        
        self.view.addSubview(segmentedControl)
        
        NSLayoutConstraint.activate([
            segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            segmentedControl.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor)
        ])
    }
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        // print the frame of segmentedControl to debug console
        print("segmentedControl Frame in viewDidLayoutSubviews():", segmentedControl.frame)
        
        segmentedControl.addBorder(
            for: .Bottom,
            color: UIColor.blue.cgColor,
            thickness: 20
        )
    }
}

extension UIView {
    enum BorderSide {
        case Left, Right, Top, Bottom
    }
    
    func addBorder(for side: BorderSide, color: CGColor, thickness: CGFloat) {
        
        let border = CALayer()
        border.backgroundColor = color
        
        switch side {
        case .Left:
            border.frame = CGRect(
                x: frame.minX,
                y: frame.minY,
                width: thickness,
                height: frame.height
            )
            break
        case .Right:
            border.frame = CGRect(
                x: frame.maxX,
                y: frame.minY,
                width: thickness,
                height: frame.height
            )
            break
        case .Top:
            border.frame = CGRect(
                x: frame.minX,
                y: frame.minY,
                width: frame.width,
                height: thickness
            )
            break
        case .Bottom:
            border.frame = CGRect(
                x: frame.minX,
                y: frame.maxY,
                width: frame.width,
                height: thickness
            )
            break
        }
        
        layer.addSublayer(border)
    }
}

现在,我们看到两个“打印帧”输出:

segmentedControl Frame in viewDidLoad(): (0.0, 0.0, 0.0, 0.0)
segmentedControl Frame in viewDidLayoutSubviews(): (0.0, 97.66666666666667, 393.0, 19.333333333333332)

不幸的是,这是结果:

蓝色层位于堆栈视图/标签底部的下方
这是因为扩展使用的是frame.maxY--它是frame.origin.y + frame.size.height--而框架的origin.y97.66666666666667(它的顶部在导航栏下面)。
您 * 可以 * 使用相同的addBorder()方法,方法是在视图布局之后(也就是说,在帧设置之后)调用它,并修改扩展以使用视图的bounds而不是frame

case .Bottom:
        border.frame = CGRect(
            x: bounds.minX,
            y: bounds.maxY,
            width: bounds.width,
            height: thickness
        )
        break

我们就得到了这个

然而......很明显,这不是一个好方法。值得注意的是,layer出现在视图的边界之外。因此,如果您添加一个约束在segmentedControl底部的子视图,**该视图的顶部将被20点高的“边界”层覆盖。
我的猜测是,你也会希望能够点击标签...可能你也想移动“边界”,以显示只在选定的标签...等。
做一些搜索/探索如何子类化UIView,以便它自己处理所有这些。

相关问题