每行文字下方画线的 UITextView, 如下图:
import UIKit
@IBDesignable
public class LinedTextView: UITextView {
@IBInspectable public var separatorLineHeight: CGFloat = 1
@IBInspectable public var separatorLineColor: UIColor = UIColor.gray
// should be called after setting all font attributes
public func getRealHeight(numberOfLine: Int) -> CGFloat {
guard let realLineHeight = realLineHeight, numberOfLine >= 0 else {
return 0
}
return realLineHeight * CGFloat(numberOfLine) + textContainerInset.top + textContainerInset.bottom + separatorLineHeight / 2
}
public var realLineHeight: CGFloat? {
guard let currentFont = font else {
return nil
}
var lineHeight: CGFloat = currentFont.lineHeight
var range: NSRange = NSRange(location: 0, length: text.lengthOfBytes(using: .unicode))
if let paragraphStyle = attributedText.attribute(NSAttributedStringKey.paragraphStyle, at: 0, effectiveRange: &range) as? NSParagraphStyle {
// priority order: maximumLineHeight > minimumLineHeight > lineHeightMultiple
if paragraphStyle.lineHeightMultiple != 0 {
lineHeight = paragraphStyle.lineHeightMultiple * currentFont.lineHeight
}
if paragraphStyle.minimumLineHeight != 0 {
lineHeight = max(lineHeight, paragraphStyle.minimumLineHeight)
}
if paragraphStyle.maximumLineHeight != 0 {
lineHeight = min(lineHeight, paragraphStyle.maximumLineHeight)
}
}
return lineHeight
}
override public func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext(), let realLineHeight = realLineHeight, realLineHeight > 0 else {
return
}
let firstVisibleLine: Int = max(1, Int(contentOffset.y / realLineHeight))
let lastVisibleLine: Int = Int(ceil((contentOffset.y + bounds.size.height) / realLineHeight))
guard firstVisibleLine < lastVisibleLine else {
return
}
context.setLineWidth(separatorLineHeight)
context.setStrokeColor(separatorLineColor.cgColor)
context.beginPath()
let startPointX: CGFloat = bounds.origin.x + textContainer.lineFragmentPadding + textContainerInset.left
let endPointX: CGFloat = bounds.size.width - textContainer.lineFragmentPadding - textContainerInset.right
for lineIndex in firstVisibleLine ..< lastVisibleLine {
let linePointY: CGFloat = textContainerInset.top + realLineHeight * CGFloat(lineIndex)
context.move(to: CGPoint(x: startPointX, y: linePointY))
context.addLine(to: CGPoint(x: endPointX, y: linePointY))
}
context.closePath()
context.strokePath()
}
// adjust position of cursor
override public func caretRect(for position: UITextPosition) -> CGRect {
guard let lineHeight = font?.lineHeight, let realLineHeight = realLineHeight, realLineHeight > 0 else {
return super.caretRect(for: position)
}
var rect = super.caretRect(for: position)
rect.origin.y = rect.origin.y + (realLineHeight - lineHeight)
rect.size.height = lineHeight
return rect
}
}