I have a custom UIView
and a table view that are all created programmatically. My custom UIView
involves a UIBezierPath
to create it's shape.
I add this UIView
to the table view tableHeaderView and it has a dynamic height. When I rotate my app from portrait to landscape the curve doesn't update. Any help would be much appreciated.
Here is all the code for the curved header view:
class ExplanationCurvedHeaderView: UIView {
public lazy var headerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.autoresizesSubviews = true
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
//view.backgroundColor = .gray
return view
}()
private lazy var headerLabel: UILabel = {
let headerLabel = UILabel()
headerLabel.translatesAutoresizingMaskIntoConstraints = false
headerLabel.numberOfLines = 0
headerLabel.lineBreakMode = .byWordWrapping
headerLabel.textAlignment = .center
headerLabel.font = .systemFont(ofSize: 18, weight: .semibold)
headerLabel.textColor = .black
headerLabel.text = "Hello this is a test Hello this is a testHello this is a testHello this is a test Hello this is a test Hello this is a test Hello this is a test Hello this is a test Hello this is a test"
return headerLabel
}()
public lazy var imageIcon: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(systemName: "exclamationmark.triangle.fill")
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
createCurve()
}
func configure() {
addSubview(headerView)
headerView.addSubview(headerLabel)
headerView.addSubview(imageIcon)
NSLayoutConstraint.activate([
headerView.topAnchor.constraint(equalTo: topAnchor, constant: -44),
headerView.leadingAnchor.constraint(equalTo: leadingAnchor),
headerView.trailingAnchor.constraint(equalTo: trailingAnchor),
headerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -80),
headerLabel.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 75),
headerLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 20),
headerLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor, constant: -20),
headerLabel.bottomAnchor.constraint(equalTo: headerView.bottomAnchor, constant: -20),
imageIcon.centerXAnchor.constraint(equalTo: centerXAnchor),
imageIcon.topAnchor.constraint(equalTo: headerView.bottomAnchor, constant: 10),
imageIcon.heightAnchor.constraint(equalToConstant: 64),
imageIcon.widthAnchor.constraint(equalToConstant: 64)
])
}
func createCurve() {
let shapeLayer = CAShapeLayer(layer: headerView.layer)
shapeLayer.path = pathForCurvedView().cgPath
//shapeLayer.frame = self.bounds
shapeLayer.frame = headerView.bounds
shapeLayer.fillColor = UIColor.lightGray.cgColor
headerView.layer.mask = shapeLayer.mask
headerView.layer.insertSublayer(shapeLayer, at: 0)
}
func pathForCurvedView() -> UIBezierPath {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: UIScreen.main.bounds.maxX, y: 0))
path.addLine(to: CGPoint(x: UIScreen.main.bounds.maxX, y: headerView.frame.height + 30))
path.addQuadCurve(to: CGPoint(x: UIScreen.main.bounds.minX, y: headerView.frame.height + 30),
controlPoint: CGPoint(x: UIScreen.main.bounds.midX, y: headerView.frame.height + 70))
print("header view height:", headerView.frame.height)
print("header label systemLayou:", headerLabel.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize))
path.close()
return path
}
}
Here is view controller code:
import UIKit
class ViewController: UIViewController {
private lazy var headerView: ExplanationCurvedHeaderView = {
let curvedView = ExplanationCurvedHeaderView(frame: .zero)
return curvedView
}()
var arrayValues: [String] = []
private lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.dataSource = self
tableView.frame = view.safeAreaLayoutGuide.layoutFrame
return tableView
}()
/*
Tried few things in this override but no correct results
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
self.headerView.headerView.setNeedsLayout()
self.headerView.createCurve()
self.tableView.setNeedsLayout()
self.tableView.setNeedsDisplay()
})
} */
override func viewDidLoad() {
for x in 1...6 {
if x.isMultiple(of: 2) {
arrayValues.append("Get To Invesin InvestinGet To Kn Get e o Kn Get eo Kn Get eo")
} else {
arrayValues.append("Get To Invesin InvestinGet To Kn Get e o Kn Get eo Kn Get eo Kn Get eo Kn Get eo Kn Get eo Kn Get eo Kn Get eo Kn Get e Get To Invesin InvestinGetGet To Invesin InvestinGetGet To Invesin InvestinGetGet To Invesin InvestinGetGet To Invesin InvestinGet nvestinGetGenvestinGetGenvestinGetGenvestinGetGenvestinGetGe")
}
}
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
//tableView.tableHeaderView = headerView
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
tableView.tableHeaderView = headerView
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = self.tableView.tableHeaderView {
let headerViewFrame = headerView.frame
let height = headerView.systemLayoutSizeFitting(headerViewFrame.size, withHorizontalFittingPriority: UILayoutPriority.defaultHigh, verticalFittingPriority: UILayoutPriority.defaultLow).height
var headerFrame = headerView.frame
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
self.tableView.tableHeaderView = headerView
}
}
headerView.layoutIfNeeded()
tableView.beginUpdates()
tableView.endUpdates()
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayValues.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CustomTableViewCell else {
return UITableViewCell()
}
cell.setValue(value: arrayValues[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
}
This also comes up in the console:
2021-04-19 22:35:19.292199-0400 HeaderViewCurve[62191:1769385] [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "<NSLayoutConstraint:0x6000022fac60 V:|-(75)-[UILabel:0x7fdce741ce10] (active, names: '|':UIView:0x7fdce741cca0 )>", "<NSLayoutConstraint:0x6000022fad50 UILabel:0x7fdce741ce10.bottom == UIView:0x7fdce741cca0.bottom - 20 (active)>", "<NSLayoutConstraint:0x6000022fab20 V:|-(-44)-[UIView:0x7fdce741cca0] (active, names: '|':HeaderViewCurve.ExplanationCurvedHeaderView:0x7fdce740cf60 )>", "<NSLayoutConstraint:0x6000022fac10 UIView:0x7fdce741cca0.bottom == HeaderViewCurve.ExplanationCurvedHeaderView:0x7fdce740cf60.bottom - 80 (active)>", "<NSLayoutConstraint:0x6000022f0460 'UIView-Encapsulated-Layout-Height' HeaderViewCurve.ExplanationCurvedHeaderView:0x7fdce740cf60.height == 0 (active)>" )
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x6000022fad50 UILabel:0x7fdce741ce10.bottom == UIView:0x7fdce741cca0.bottom - 20 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. 2021-04-19 22:35:19.293129-0400 HeaderViewCurve[62191:1769385] [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "<NSLayoutConstraint:0x6000022fab20 V:|-(-44)-[UIView:0x7fdce741cca0] (active, names: '|':HeaderViewCurve.ExplanationCurvedHeaderView:0x7fdce740cf60 )>", "<NSLayoutConstraint:0x6000022fac10 UIView:0x7fdce741cca0.bottom == HeaderViewCurve.ExplanationCurvedHeaderView:0x7fdce740cf60.bottom - 80 (active)>", "<NSLayoutConstraint:0x6000022f0460 'UIView-Encapsulated-Layout-Height' HeaderViewCurve.ExplanationCurvedHeaderView:0x7fdce740cf60.height == 0 (active)>" )
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x6000022fac10 UIView:0x7fdce741cca0.bottom == HeaderViewCurve.ExplanationCurvedHeaderView:0x7fdce740cf60.bottom - 80 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.