If you override draw(_ rect: CGRect)
in a subclass, it is up to you to draw the view content. Setting .backgroundColor there will have no effect.
This is a common structure for a UIView
subclass:
class View1Class: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
self.backgroundColor = .red
// do any other view setup here, such as
// adding subviews, adding sublayers, etc
}
}
Edit
Here is an example of two custom views - the first one uses shape layers, the second one overrides draw()
:
MyFirstView class
class MyFirstView: UIView {
let boxLayer = CAShapeLayer()
let circleLayer = CAShapeLayer()
let lineLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
self.backgroundColor = .systemYellow
layer.addSublayer(boxLayer)
layer.addSublayer(circleLayer)
layer.addSublayer(lineLayer)
boxLayer.fillColor = UIColor.red.cgColor
circleLayer.fillColor = UIColor.blue.cgColor
lineLayer.strokeColor = UIColor.green.cgColor
}
override func layoutSubviews() {
super.layoutSubviews()
// centered rectangle, half height and width
let halfWidth: CGFloat = bounds.width * 0.5
let halfHeight: CGFloat = bounds.height * 0.5
let boxRect: CGRect = CGRect(x: halfWidth * 0.5, y: halfHeight * 0.5, width: halfWidth, height: halfHeight)
let boxPath: UIBezierPath = UIBezierPath(rect: boxRect)
boxLayer.path = boxPath.cgPath
// circle centered in box, 3/4ths of the shorter of width or height
let wh: CGFloat = min(boxRect.width, boxRect.height) * 0.75
let circleRect: CGRect = CGRect(x: boxRect.midX - wh * 0.5, y: boxRect.midY - wh * 0.5, width: wh, height: wh)
let circlePath: UIBezierPath = UIBezierPath(ovalIn: circleRect)
circleLayer.path = circlePath.cgPath
// diagonal line from top-left to bottom-right
let linePath: UIBezierPath = UIBezierPath()
linePath.move(to: .zero)
linePath.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
lineLayer.path = linePath.cgPath
}
}
MySecondView class
class MySecondView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
self.backgroundColor = .systemTeal
}
override func draw(_ rect: CGRect) {
// centered rectangle, half height and width
let halfWidth: CGFloat = rect.width * 0.5
let halfHeight: CGFloat = rect.height * 0.5
let boxRect: CGRect = CGRect(x: halfWidth * 0.5, y: halfHeight * 0.5, width: halfWidth, height: halfHeight)
let boxPath: UIBezierPath = UIBezierPath(rect: boxRect)
UIColor.red.setFill()
boxPath.fill()
// circle centered in box, 3/4ths of the shorter of width or height
let wh: CGFloat = min(boxRect.width, boxRect.height) * 0.75
let circleRect: CGRect = CGRect(x: boxRect.midX - wh * 0.5, y: boxRect.midY - wh * 0.5, width: wh, height: wh)
let circlePath: UIBezierPath = UIBezierPath(ovalIn: circleRect)
UIColor.blue.setFill()
circlePath.fill()
// diagonal line from top-left to bottom-right
let linePath: UIBezierPath = UIBezierPath()
linePath.move(to: .zero)
linePath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
UIColor.green.setStroke()
linePath.stroke()
}
override func layoutSubviews() {
setNeedsDisplay()
}
}
Example view controller
class CustomViewsViewController: UIViewController {
let firstView = MyFirstView()
let secondView = MySecondView()
override func viewDidLoad() {
super.viewDidLoad()
[firstView, secondView].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// firstView Top / Leading / Trailing to view (safe-area)
firstView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
firstView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
firstView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
// firstView Height
firstView.heightAnchor.constraint(equalToConstant: 150.0),
// secondView Leading / Trailing to view (safe-area)
secondView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
secondView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
// secondView Top 20-pts from firstView Bottom
secondView.topAnchor.constraint(equalTo: firstView.bottomAnchor, constant: 20.0),
// secondView Bottom to View Bottom (safe-area)
secondView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
}
}
Output - first view has .systemYellow
background, second view has .systemTeal
background:
and rotated: