【问题标题】:Can't draw a line on a UIView无法在 UIView 上画线
【发布时间】:2020-06-07 18:06:20
【问题描述】:

我有一个包含两个 UITextfields 的 UIView,我想在两者之间创建一个 1.0 像素宽度的分隔符,但我无法让它工作。 我尝试覆盖draw func,但没有成功,我也尝试使用bezierPath和shapeLayer,但我没有UIView的框架,所以无法绘制bezierPath。

这是我拥有的绘制覆盖:

override func draw(_ rect: CGRect) {
    super.draw(rect)

    if let context = UIGraphicsGetCurrentContext() {
        context.setStrokeColor(UIColor.black.cgColor)
        context.setLineWidth(1)
        context.move(to: CGPoint(x: bounds.width / 2, y: 0))
        context.addLine(to: CGPoint(x: bounds.width / 2, y: bounds.height))
        context.strokePath()
    }
}

这是 bezierPath 函数:

private func drawSeparator() {
    let path = UIBezierPath()
    path.addLine(to: CGPoint(x: bounds.width / 2, y: 0))
    path.move(to: CGPoint(x: bounds.width / 2, y: self.bounds.height))

    let shape = CAShapeLayer()
    shape.path = path.cgPath
    shape.strokeColor = UIColor.appGray.cgColor
    shape.lineWidth = 1.0
    shape.fillColor = UIColor.appGray.cgColor

    self.layer.addSublayer(shape)
}

我还尝试将 path 变量移动到类级别,并将其用作惰性 var 以尝试以这种方式获取框架/边界,但它也不起作用。

我正在尝试创建一个简单的垂直分隔符。

【问题讨论】:

    标签: ios


    【解决方案1】:

    虽然我建议进行一些改进,但您的 draw(:_) 演绎版效果很好。 (你的形状层渲染是错误的......你想将形状层的创建和路径的更新解耦;但让我们把它放在一边。)

    如果您没有在 draw(_:) 再现中看到视图的分隔线,则问题出在其他地方。尝试在 draw 方法中插入日志语句,并确保您按您认为应该的方式访问此代码,并且 bounds 是您认为应该的。或者使用视图调试器来确认基类设置是否正确以及框架是你认为的那样,等等。


    我可能建议使用midXminYmaxY。就个人而言,除非必要,否则我也不会使用 CoreGraphics:

    @IBDesignable
    class VerticalSplitView: UIView {
        override func draw(_ rect: CGRect) {
            super.draw(rect)
    
            let path = UIBezierPath()
            path.move(to: CGPoint(x: bounds.midX, y: bounds.minY))
            path.addLine(to: CGPoint(x: bounds.midX, y: bounds.maxY))
            path.lineWidth = 1
    
            UIColor.black.setStroke()
            path.stroke()
        }
    }
    

    顺便说一句,如果您使用这种draw(_:) 方法,请确保将视图的内容模式设置为redraw。如果没有这个,你会得到奇怪的行为(例如,如果约束将此视图的宽度链接到它的父视图并且用户旋转设备,它将拉伸这条垂直线的宽度)。


    就个人而言,我喜欢摆脱手动绘图并将其交给CAShapeLayer。这样我就不必担心视图的contentMode 并将绘图卸载到 Apple 的代码中(毫无疑问,这里有我们不考虑的优化)。

    正如我所提到的,您必须将形状层的创建(在初始化期间完成)与其路径的更新(应该在 layoutSubviews 中完成)分离。

    就个人而言,我可能有一个支持层是CAShapeLayer的视图:

    @IBDesignable
    class VerticalShapeLayer: UIView {
        override class var layerClass: AnyClass { return CAShapeLayer.self }
        lazy var shapeLayer: CAShapeLayer = layer as! CAShapeLayer
        
        override func layoutSubviews() {
            super.layoutSubviews()
    
            let path = UIBezierPath()
            path.move(to: CGPoint(x: bounds.midX, y: bounds.minY))
            path.addLine(to: CGPoint(x: bounds.midX, y: bounds.maxY))
    
            shapeLayer.strokeColor = UIColor { traitCollection in
                switch traitCollection.userInterfaceStyle {
                case .dark: return .white
                default:    return .black
                }
            }.cgColor
            shapeLayer.lineWidth = 1
            shapeLayer.fillColor = UIColor.clear.cgColor
            shapeLayer.path = path.cgPath
        }
    }
    

    另外,作为改进,我会根据设备是否处于暗模式来设置笔划的颜色。这显然假设您也相应地设置了背景颜色。对描边颜色做任何你想做的事情。


    一旦你解决了直接的问题,最后一个建议是:如果你使用的是 NIB 或故事板,我会将这个视图类指定为 @IBDesignable。这样我就可以在 Interface Builder 中直接看到它,从而消除了很多这样的问题。我们可以看到它在设计过程中呈现,消除了基类是否设置正确、框架是否正确等方面的歧义。

    当然没有必要,但@IBDesignable 有助于在设计过程中发现问题。

    【讨论】:

      【解决方案2】:

      目前尚不清楚您希望做什么,但这是一个直接基于您的代码的完整工作版本:

      class MyView: UIView {
          override init(frame: CGRect) {
              super.init(frame: frame)
              self.isOpaque = false
          }
          required init?(coder aDecoder: NSCoder) {
              fatalError("init(coder:) has not been implemented")
          }
          override func draw(_ rect: CGRect) {
              super.draw(rect)
              if let context = UIGraphicsGetCurrentContext() {
                  context.setStrokeColor(UIColor.black.cgColor)
                  context.setLineWidth(1)
                  context.move(to: CGPoint(x: bounds.width / 2, y: 0))
                  context.addLine(to: CGPoint(x: bounds.width / 2, y: bounds.height))
                  context.strokePath()
              }
          }
      }
      
      class ViewController: UIViewController {
          let myView = MyView()
          override func viewDidLayoutSubviews() {
              super.viewDidLayoutSubviews()
              if myView.superview == nil {
                  self.view.addSubview(myView)
              }
              myView.frame = self.view.bounds
              myView.setNeedsDisplay()
          }
      }
      

      看起来像这样:

      好的,既然您已经看到了一个有效的示例,请根据您自己的情况调整它,一切就绪。

      【讨论】:

        【解决方案3】:

        对于行也可以使用UIView在textField之间作为分隔符(设置UIView的宽度)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-09-22
          • 1970-01-01
          • 2011-03-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多