One approach is to create a UITextView
subclass and override draw(_ rect: CGRect)
.
Here is an example that should get you on your way:
class LinedTextView: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// this is needed to tell the lines to redraw
// when the text is scrolled
self.contentMode = .redraw
}
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext(),
let font = self.font
else {
super.draw(rect)
return
}
context.setStrokeColor(UIColor.red.cgColor)
context.setLineWidth(1.0)
context.beginPath()
let nLines = (self.contentSize.height + self.bounds.height) / font.lineHeight
var y: CGFloat = font.lineHeight + textContainerInset.top
for _ in 0..<Int(nLines) {
context.move(to: .init(x: 0.0, y: y))
context.addLine(to: .init(x: bounds.maxX, y: y))
y += font.lineHeight
}
context.strokePath()
}
}
You use it just like a normal UITextView
as in this example view controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testTextView = LinedTextView()
testTextView.backgroundColor = .yellow
testTextView.text = "When a user taps a text view, a keyboard appears; when a user taps Return in the keyboard, the keyboard disappears and the text view can handle the input in an application-specific way. You can specify attributes, such as font, color, and alignment, that apply to all text in a text view."
testTextView.font = .systemFont(ofSize: 28.0, weight: .regular)
testTextView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(testTextView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
testTextView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
testTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
testTextView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
testTextView.heightAnchor.constraint(equalToConstant: 300.0),
])
}
}
Looks like this when running:

Please note: this is Example Code Only!!! - it is intended to give you something to learn from and should not be considered "production ready."