scrollview scrolling on iphone x simulator but not other devices - ios

I have the following code which sets up the layout of a signup view.
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.mainWhite()
scrollView.isScrollEnabled = true
scrollView.contentSize = CGSize(width: view.frame.size.width, height: view.frame.size.height)
setupView()
addKeyboardObservers()
assignFirstResponders()
}
func setupView() {
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
scrollView.addSubview(logoView)
logoView.anchor(top: scrollView.topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 10, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 110, height: 110)
logoView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
let stackView = UIStackView(arrangedSubviews: [emailTextField, passwordTextField, confirmPasswordTextField, firstNameTextField, lastNameTextField, phoneNumberTextField , signupButton])
stackView.axis = .vertical
stackView.spacing = 4
stackView.distribution = .fillEqually
scrollView.addSubview(stackView)
stackView.anchor(top: logoView.bottomAnchor, left: scrollView.leftAnchor, bottom: nil, right: scrollView.rightAnchor, paddingTop: 8, paddingLeft: 60, paddingBottom: 0, paddingRight: 60, width: 0, height: 350)
stackView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
scrollView.addSubview(alreadyHaveAccountButton)
alreadyHaveAccountButton.anchor(top: stackView.bottomAnchor, left: scrollView.leftAnchor, bottom: nil, right: scrollView.rightAnchor, paddingTop: 12, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 50)
scrollView.addSubview(footerView)
footerView.translatesAutoresizingMaskIntoConstraints = false
footerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
footerView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
footerView.heightAnchor.constraint(equalToConstant: 70).isActive = true
footerView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
footerView.addSubview(termsAndPrivacyButton)
termsAndPrivacyButton.translatesAutoresizingMaskIntoConstraints = false
termsAndPrivacyButton.centerXAnchor.constraint(equalTo: footerView.centerXAnchor).isActive = true
termsAndPrivacyButton.centerYAnchor.constraint(equalTo: footerView.centerYAnchor).isActive = true
}
func assignFirstResponders() {
emailTextField.delegate = self
passwordTextField.delegate = self
confirmPasswordTextField.delegate = self
firstNameTextField.delegate = self
lastNameTextField.delegate = self
phoneNumberTextField.delegate = self
}
I need the view to scroll when the keyboard overlaps an input view. And I've seen plenty of solutions for that here that address that.
extension SignupViewController {
func addKeyboardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(SignupViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(SignupViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
scrollView.isScrollEnabled = true
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height + 15
scrollView.contentInset = contentInset
print("content inset: \(scrollView.contentInset) \nKeyboard frame \(keyboardFrame)")
}
#objc func keyboardWillHide(notification: NSNotification) {
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
scrollView.contentInset = contentInset
scrollView.isScrollEnabled = false
}
}
The issue I'm encountering now is that the scrolling functionality is ONLY working on the iPhone X simulator. When I test on my actual iPhone 6s device, the scrolling does not work. I've tried all all simualtor devices and the scrollign only works on the iphone X simulator which is extremely strange. Has anyone encountered something like this?

Related

Cant get dynamic scroll view that sizes to its content working working

I'm having an issue getting my scroll view to to dynamically size and scroll to fit all of my content. I'm doing it all programmatically as I find it the easiest to use when dealing with auto layout. Anyways every solution I've come across online doesn't seem to work and the closest I got was when I tried this. contentView.heightAnchor.constraint(equalTo: self.scrollView.frameLayoutGuide.heightAnchor).isActive = true Setting the height anchor of the context view to the frame layout guide of the scroll view allowed me to scroll but it still didn't reach the bottom. I feel like I must be overlooking something but I can't figure it out for the life of me. Here is my code below.Thanks Also sorry about how it's displayed below I can't get it to all fit in the box.
Edit: Prior to posting this I was experimenting with embedding my content view in a stack view which is why it's there but I have tried it without the stack and it still didn't work.
Edit 2: Found a solution and fixed how the code was displayed.
import UIKit
import Charts
class ViewController: UIViewController, UIScrollViewDelegate, ChartViewDelegate {
let scrollView = UIScrollView()
let contentStackView = UIStackView()
let contentView = UIView()
var dealStack, customScoreStack, basicInfoStack: UIStackView!
var thisListing = Listing()
var priceDollarSign, dealIndicatorArrow, vehicleImage, customScoreLogo: UIImageView!
var vehiclePrice, dealType, dealValueLbl, milesLbl, customScoreValue, customScoreTitle, basicCarInfo, distanceAndDealer, graphTitle, specTitle, insuranceCalcTitle, commentsTitle, similarListingsTitle: UILabel!
var graphSwitchingSegment, yearSwitchingSegment1, yearSwitchingSegment2: UISegmentedControl!
var carPrice = "", dealValue = "1", miles = "", year = "2021", make = "GMC", model = "YUKON", trim = "DENALI", distance = "20", dealerName = "Dealer"
var graphState = true
var graphData1, graphData2: String!
lazy var graph1: LineChartView = {
let chartView = LineChartView()
return chartView
}()
lazy var graph2: LineChartView = {
let chartView = LineChartView()
return chartView
}()
var months: [String]!
var yValues: [ChartDataEntry]!
override func viewDidLoad() {
super.viewDidLoad()
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
yValues = [
ChartDataEntry(x: 1, y: 20.0),
ChartDataEntry(x: 2, y: 4.0),
ChartDataEntry(x: 3, y: 6.0),
ChartDataEntry(x: 4, y: 3.0),
ChartDataEntry(x: 5, y: 12.0),
ChartDataEntry(x: 6, y: 16.0),
ChartDataEntry(x: 7, y: 4.0),
ChartDataEntry(x: 8, y: 18.0),
ChartDataEntry(x: 9, y: 2.0),
ChartDataEntry(x: 10, y: 4.0),
ChartDataEntry(x: 11, y: 5.0),
ChartDataEntry(x: 12, y: 4.0)
]
self.scrollView.backgroundColor = .lightGray
self.view.addSubview(scrollView)
self.view.sendSubviewToBack(scrollView)
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.isScrollEnabled = true
self.scrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
self.scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.scrollView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.scrollView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor).isActive = true
self.scrollView.addSubview(contentStackView)
self.contentStackView.translatesAutoresizingMaskIntoConstraints = false
self.contentStackView.axis = .vertical
self.contentStackView.topAnchor.constraint(equalTo: self.scrollView.topAnchor).isActive = true
self.contentStackView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor).isActive = true
self.contentStackView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor).isActive = true
self.contentStackView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor).isActive = true
self.contentStackView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
contentStackView.addArrangedSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.topAnchor.constraint(equalTo: self.contentStackView.topAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: self.contentStackView.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: self.contentStackView.trailingAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: self.contentStackView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: self.contentStackView.widthAnchor).isActive = true
//contentView.heightAnchor.constraint(equalTo: self.scrollView.frameLayoutGuide.heightAnchor).isActive = true
contentView.sizeToFit()
setUpView()
graph1Setup()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.delegate = self
print("View Size: \(self.view.frame.debugDescription) \nScroll View Size: \(self.scrollView.frame.debugDescription) \nScroll Content View Size: \(self.contentView.frame.debugDescription)")
}
override func viewDidAppear(_ animated: Bool) {
// Test subview locations here: print(_SUBVIEW.frame.debugDescription)
}
#objc func graphSwitcher(_ segmentedControl: UISegmentedControl) {
switch graphSwitchingSegment.selectedSegmentIndex {
case 0:
graphState = true
print("VALUE CHANGED")
print(self.contentView.frame.debugDescription)
case 1:
graphState = false
print("VALUE CHANGED")
default:
graphState = true
break
}
}
func setUpView() {
//Object Instantiation
dealStack = UIStackView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
dealStack.translatesAutoresizingMaskIntoConstraints = false
dealStack.axis = .vertical
dealStack.spacing = 1
dealStack.distribution = .fillProportionally
customScoreStack = UIStackView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
customScoreStack.translatesAutoresizingMaskIntoConstraints = false
customScoreStack.axis = .vertical
customScoreStack.spacing = 1
customScoreStack.distribution = .fillProportionally
basicInfoStack = UIStackView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
basicInfoStack.translatesAutoresizingMaskIntoConstraints = false
basicInfoStack.axis = .vertical
basicInfoStack.spacing = 2
basicInfoStack.distribution = .fillProportionally
priceDollarSign = UIImageView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
priceDollarSign.image = UIImage(named: "greenDollarsign")
priceDollarSign.translatesAutoresizingMaskIntoConstraints = false
dealIndicatorArrow = UIImageView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
dealIndicatorArrow.image = UIImage(named: "greenUpArrow")
dealIndicatorArrow.translatesAutoresizingMaskIntoConstraints = false
vehicleImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
vehicleImage.image = UIImage(named: "audiA7")
vehicleImage.translatesAutoresizingMaskIntoConstraints = false
customScoreLogo = UIImageView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
customScoreLogo.image = UIImage(named: "aladdinGenie")
customScoreLogo.translatesAutoresizingMaskIntoConstraints = false
customScoreTitle = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
customScoreTitle.text = "Genie Score:"
customScoreTitle.textAlignment = .right
customScoreTitle.translatesAutoresizingMaskIntoConstraints = false
customScoreValue = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
customScoreValue.text = "VALUE_PLACE_HOLDER"
customScoreValue.translatesAutoresizingMaskIntoConstraints = false
vehiclePrice = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
vehiclePrice.text = numberFormatter.string(from: NSNumber(value: thisListing.getDetails().returnCarPrice()))
vehiclePrice.font = UIFont.systemFont(ofSize: 30, weight: .semibold)
vehiclePrice.textAlignment = .left
vehiclePrice.translatesAutoresizingMaskIntoConstraints = false
dealType = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
dealType.text = thisListing.getDetails().returnDealRatingType().0
dealType.font = UIFont(name: dealType.font.fontName, size: 28)
dealType.textColor = self.thisListing.getDetails().returnDealRatingType().1
dealType.adjustsFontSizeToFitWidth = true
dealType.minimumScaleFactor = 0.8
dealType.numberOfLines = 0
dealType.textAlignment = .right
dealType.translatesAutoresizingMaskIntoConstraints = false
dealValueLbl = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
dealValueLbl.text = "$" + (numberFormatter.string(from: NSNumber(value: self.thisListing.getDetails().returnDealDiff()))!) + (self.thisListing.getDetails().returnDealDiff() > 0 ? " ABOVE":" BELOW")
dealValueLbl.font = UIFont(name: dealValueLbl.font.fontName, size: 19)
dealValueLbl.adjustsFontSizeToFitWidth = true
dealValueLbl.minimumScaleFactor = 0.8
dealValueLbl.numberOfLines = 0
dealValueLbl.textAlignment = .right
dealValueLbl.translatesAutoresizingMaskIntoConstraints = false
milesLbl = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
milesLbl.font = UIFont.systemFont(ofSize: 34, weight: .regular)
milesLbl.textAlignment = .left
milesLbl.adjustsFontSizeToFitWidth = true
milesLbl.minimumScaleFactor = 0.8
milesLbl.text = "\(numberFormatter.string(from: NSNumber(value: (self.miles as NSString).integerValue)) ?? "") Miles"
milesLbl.translatesAutoresizingMaskIntoConstraints = false
basicCarInfo = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
basicCarInfo.text = "\(year) \(make) \(model) \(trim)"
basicCarInfo.font = UIFont.systemFont(ofSize: 20, weight: .semibold)
basicCarInfo.adjustsFontSizeToFitWidth = true
basicCarInfo.numberOfLines = 0
basicCarInfo.minimumScaleFactor = 0.8
basicCarInfo.textAlignment = .center
basicCarInfo.translatesAutoresizingMaskIntoConstraints = false
distanceAndDealer = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
distanceAndDealer.text = "\(distance) miles away from you at \(dealerName)"
distanceAndDealer.font = UIFont.systemFont(ofSize: 20, weight: .regular)
distanceAndDealer.adjustsFontSizeToFitWidth = true
distanceAndDealer.numberOfLines = 0
distanceAndDealer.minimumScaleFactor = 0.8
distanceAndDealer.textAlignment = .center
distanceAndDealer.translatesAutoresizingMaskIntoConstraints = false
graphTitle = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
graphTitle.text = "Graphs"
graphTitle.font = UIFont.systemFont(ofSize: 22, weight: .semibold)
graphTitle.numberOfLines = 0
graphTitle.adjustsFontSizeToFitWidth = true
graphTitle.minimumScaleFactor = 0.8
graphTitle.textAlignment = .center
graphTitle.translatesAutoresizingMaskIntoConstraints = false
graphSwitchingSegment = UISegmentedControl(items: ["Graph 1","Graph 2"])
graphSwitchingSegment.addTarget(self, action: #selector(graphSwitcher(_:)), for: .valueChanged)
graphSwitchingSegment.selectedSegmentIndex = 0
graphSwitchingSegment.translatesAutoresizingMaskIntoConstraints = false
graph1.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
graph1.noDataText = "OOPS NO DATA HERE!"
graph1.noDataTextColor = .green
graph1.backgroundColor = .white
graph1.translatesAutoresizingMaskIntoConstraints = false
yearSwitchingSegment1 = UISegmentedControl(items: ["Year 1","Year 2"])
yearSwitchingSegment1.addTarget(self, action: #selector(graphSwitcher(_:)), for: .valueChanged)
yearSwitchingSegment1.selectedSegmentIndex = 0
yearSwitchingSegment1.translatesAutoresizingMaskIntoConstraints = false
//Adds objects to the view
self.contentView.addSubview(priceDollarSign)
self.contentView.addSubview(dealStack)
self.contentView.addSubview(customScoreStack)
self.contentView.addSubview(basicInfoStack)
self.contentView.addSubview(dealIndicatorArrow)
self.contentView.addSubview(vehicleImage)
self.contentView.addSubview(vehiclePrice)
self.contentView.addSubview(milesLbl)
self.contentView.addSubview(customScoreLogo)
self.contentView.addSubview(graphTitle)
self.contentView.addSubview(graphSwitchingSegment)
self.contentView.addSubview(graph1)
self.contentView.addSubview(yearSwitchingSegment1)
dealStack.addArrangedSubview(dealType)
dealStack.addArrangedSubview(dealValueLbl)
customScoreStack.addArrangedSubview(customScoreTitle)
customScoreStack.addArrangedSubview(customScoreValue)
basicInfoStack.addArrangedSubview(basicCarInfo)
basicInfoStack.addArrangedSubview(distanceAndDealer)
//Object Constraints
priceDollarSign.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 7).isActive = true
priceDollarSign.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 7).isActive = true
priceDollarSign.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.1).isActive = true
priceDollarSign.widthAnchor.constraint(greaterThanOrEqualToConstant: 40).isActive = true
priceDollarSign.widthAnchor.constraint(lessThanOrEqualToConstant: 100).isActive = true
priceDollarSign.heightAnchor.constraint(equalTo: priceDollarSign.widthAnchor).isActive = true
priceDollarSign.heightAnchor.constraint(greaterThanOrEqualToConstant: 40).isActive = true
priceDollarSign.heightAnchor.constraint(lessThanOrEqualToConstant: 100).isActive = true
vehiclePrice.topAnchor.constraint(equalTo: priceDollarSign.topAnchor).isActive = true
vehiclePrice.bottomAnchor.constraint(equalTo: priceDollarSign.bottomAnchor).isActive = true
vehiclePrice.leadingAnchor.constraint(equalTo: priceDollarSign.trailingAnchor, constant: 8).isActive = true
vehiclePrice.sizeToFit()
vehiclePrice.layoutIfNeeded()
dealStack.topAnchor.constraint(equalTo: priceDollarSign.topAnchor).isActive = true
dealStack.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: -7).isActive = true
dealStack.heightAnchor.constraint(equalTo: priceDollarSign.heightAnchor).isActive = true
dealType.heightAnchor.constraint(equalTo: dealValueLbl.heightAnchor).isActive = true
dealType.widthAnchor.constraint(equalTo: dealValueLbl.widthAnchor).isActive = true
dealType.sizeToFit()
dealType.layoutIfNeeded()
dealValueLbl.sizeToFit()
dealValueLbl.layoutIfNeeded()
dealIndicatorArrow.topAnchor.constraint(equalTo: priceDollarSign.topAnchor).isActive = true
dealIndicatorArrow.bottomAnchor.constraint(equalTo: priceDollarSign.bottomAnchor).isActive = true
dealIndicatorArrow.trailingAnchor.constraint(equalTo: dealType.leadingAnchor, constant: -1).isActive = true
dealIndicatorArrow.widthAnchor.constraint(equalTo: priceDollarSign.widthAnchor).isActive = true
vehicleImage.topAnchor.constraint(equalTo: priceDollarSign.bottomAnchor, constant: 8).isActive = true
vehicleImage.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor).isActive = true
vehicleImage.widthAnchor.constraint(equalTo: self.contentView.widthAnchor).isActive = true
vehicleImage.heightAnchor.constraint(equalTo: self.vehicleImage.widthAnchor, multiplier: 9/16).isActive = true
vehicleImage.heightAnchor.constraint(lessThanOrEqualToConstant: self.view.frame.height * 0.3).isActive = true
vehicleImage.contentMode = .scaleAspectFit
milesLbl.topAnchor.constraint(equalTo: vehicleImage.bottomAnchor, constant: 8).isActive = true
milesLbl.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 8).isActive = true
milesLbl.sizeToFit()
milesLbl.layoutIfNeeded()
customScoreStack.topAnchor.constraint(equalTo: milesLbl.topAnchor).isActive = true
customScoreStack.heightAnchor.constraint(equalTo: milesLbl.heightAnchor).isActive = true
customScoreStack.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
customScoreLogo.topAnchor.constraint(equalTo: milesLbl.topAnchor).isActive = true
customScoreLogo.trailingAnchor.constraint(equalTo: customScoreStack.leadingAnchor, constant: 1).isActive = true
customScoreLogo.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.1).isActive = true
customScoreLogo.widthAnchor.constraint(greaterThanOrEqualToConstant: 40).isActive = true
customScoreLogo.widthAnchor.constraint(lessThanOrEqualToConstant: 100).isActive = true
customScoreLogo.heightAnchor.constraint(equalTo: customScoreLogo.widthAnchor).isActive = true
customScoreLogo.heightAnchor.constraint(greaterThanOrEqualToConstant: 40).isActive = true
customScoreLogo.heightAnchor.constraint(lessThanOrEqualToConstant: 100).isActive = true
basicInfoStack.topAnchor.constraint(equalTo: milesLbl.bottomAnchor, constant: 32).isActive = true
basicInfoStack.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
basicInfoStack.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, constant: -10).isActive = true
basicCarInfo.heightAnchor.constraint(equalTo: distanceAndDealer.heightAnchor).isActive = true
graphTitle.topAnchor.constraint(equalTo: basicInfoStack.bottomAnchor, constant: 24).isActive = true
graphTitle.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
graphTitle.widthAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.widthAnchor, constant: -16).isActive = true
graphSwitchingSegment.topAnchor.constraint(equalTo: graphTitle.bottomAnchor, constant: 20).isActive = true
graphSwitchingSegment.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
graphSwitchingSegment.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
graph1.topAnchor.constraint(equalTo: graphSwitchingSegment.bottomAnchor, constant: 2).isActive = true
graph1.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
graph1.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
graph1.heightAnchor.constraint(equalTo: self.graph1.widthAnchor, multiplier: 12/16).isActive = true
yearSwitchingSegment1.topAnchor.constraint(equalTo: graph1.bottomAnchor, constant: 100).isActive = true
yearSwitchingSegment1.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
yearSwitchingSegment1.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
}
func graph1Setup() {
let dataSet = LineChartDataSet(entries: yValues, label: "Months")
let data = LineChartData(dataSet: dataSet)
graph1.data = data
}
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
print(entry)
}
}
I found a solution that worked and turns out it was something small that I missed which blocked me for hours. I created a UIScrollView and UIView extension with a function that sorts the subviews by maxY of each and resizes the content view and height to be equal to that. The reason it wasn't working earlier is because the scroll view only had one subview which was a container view that housed all of the UI components. I'm not sure exactly why the container view still wasn't growing with it's subviews because whenever I would get rid of any height constraints it would end up being zero and the sub views would just be out of the bounds but with this fix it seems to work fine now. Heres what I did and the link to where I found help.
Extensions
extension UIScrollView {
func updateContentView() {
contentSize.height = subviews.sorted(by: { $0.frame.maxY < $1.frame.maxY }).last?.frame.maxY ?? contentSize.height
}
}
extension UIView {
func updateHeightToFitContent() {
frame.size.height = subviews.sorted(by: { $0.frame.maxY < $1.frame.maxY }).last?.frame.maxY ?? frame.size.height
}
}
viewDidLayoutSubviews()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.delegate = self
self.view.layoutIfNeeded() //MAKE SURE YOU ADD THIS IT'S NEEDED
contentView.updateHeightToFitContent()
scrollView.updateContentView()
}
You don't need to do any height calculating... nor do you need to be setting contentSize.height.
Give the bottom element a bottom constraint.
In the code you've shown, add this line at the end of your constraints setup:
yearSwitchingSegment1.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -20).isActive = true
After adding that line, get rid of both of those extensions, and get rid of viewDidLayoutSubviews() - you can put scrollView.delegate = self in viewDidLoad().
I used some constant strings since I don't have your
`var thisListing = Listing()`
code, but this is what I get WITHOUT that constraint:
and cannot scroll.
This is what I get WITH that constraint, and as you can see, I can now scroll to the bottom of your content:

How to segue smoothly from UITableViewController to UIViewController programmatically

I'm not using storyboard and I'm doing everything programmatically.
So my problem is that when try to run the app on my phone (This does not happen when I use the simulator in Xcode), and when I use the following method...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationVC = ThirdViewController()
destinationVC.selectedExercise = exercises![indexPath.row]
self.navigationController?.pushViewController(destinationVC, animated: true)
}
...to transition from a UITableView to a UIView, the contents of the View transition normally, but the background freezes for a second before transitioning to the UIView.
What's causing this and how can I fix this?
Here is the code for the viewController that I'm transitioning to.
import UIKit
import RealmSwift
class ThirdViewController: UIViewController, UITextViewDelegate {
let realm = try! Realm()
var stats : Results<WeightSetsReps>?
var weightTextField = UITextField()
var weightLabel = UILabel()
var notesTextView = UITextView()
var repsTextField = UITextField()
var repsLabel = UILabel()
var timerImage = UIImageView()
var nextSet = UIButton()
var nextExcersise = UIButton()
var selectedExercise : Exercises? {
didSet{
loadWsr()
}
}
//MARK: - ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
notesTextView.delegate = self
timeClock()
navConAcc()
labelConfig()
setTextFieldConstraints()
setImageViewConstraints()
setTextViewConstraints()
setButtonConstraints()
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
//MARK: - UILabel
func labelConfig(){
weightTextField.placeholder = "Total weight..."
weightTextField.layer.borderWidth = 1
weightTextField.backgroundColor = .white
weightTextField.layer.cornerRadius = 25
weightTextField.layer.borderColor = UIColor.lightGray.cgColor
weightLabel.text = " Weight (lbs): "
weightLabel.textColor = .black
weightTextField.leftView = weightLabel
weightTextField.leftViewMode = .always
repsTextField.placeholder = "Number of Reps..."
repsTextField.layer.borderWidth = 1
repsTextField.backgroundColor = .white
repsTextField.layer.cornerRadius = 25
repsTextField.layer.borderColor = UIColor.lightGray.cgColor
repsLabel.text = " Repetitions: "
repsLabel.textColor = .black
notesTextView.layer.borderWidth = 1
notesTextView.backgroundColor = .white
notesTextView.layer.cornerRadius = 25
notesTextView.layer.borderColor = UIColor.lightGray.cgColor
notesTextView.text = " Notes..."
notesTextView.textColor = UIColor.lightGray
notesTextView.returnKeyType = .done
repsTextField.leftView = repsLabel
repsTextField.leftViewMode = .always
nextSet.layer.borderWidth = 1
nextSet.backgroundColor = .white
nextSet.layer.cornerRadius = 25
nextSet.layer.borderColor = UIColor.lightGray.cgColor
nextSet.setTitle("Next Set", for: .normal)
nextSet.setTitleColor(.black, for: .normal)
nextSet.addTarget(self, action: #selector(addNewSet), for: .touchUpInside)
nextExcersise.layer.borderWidth = 1
nextExcersise.backgroundColor = .white
nextExcersise.layer.cornerRadius = 25
nextExcersise.layer.borderColor = UIColor.lightGray.cgColor
nextExcersise.setTitle("Next Exercise", for: .normal)
nextExcersise.setTitleColor(.black, for: .normal)
[weightTextField, repsTextField, notesTextView, nextSet, nextExcersise].forEach{view.addSubview($0)}
}
//MARK: - TextView Delegates
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.text == " Notes..." {
textView.text = ""
textView.textColor = UIColor.black
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
}
return true
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text == ""{
notesTextView.text = " Notes..."
notesTextView.layer.borderColor = UIColor.lightGray.cgColor
}
}
//MARK: - Dismiss Keyboard Function
#objc func dismissKeyboard(){
view.endEditing(true)
}
//MARK: - TextField Constraints
func setTextFieldConstraints(){
weightTextField.anchor(top: view.safeAreaLayoutGuide.topAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor,padding: .init(top: 20, left: 40, bottom: 0, right: -40), size: .init(width: 0, height: 50))
repsTextField.anchor(top: weightTextField.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 30, left: 40, bottom: 0, right: -40) ,size: .init(width: 0, height: 50))
}
//MARK: - UIButton Functions
#objc func addNewSet(){
print("It Works")
}
//MARK: - UIButton Constraints
func setButtonConstraints(){
nextSet.anchor(top: nil, leading: view.leadingAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, trailing: nil, padding: .init(top: 0, left: 40, bottom: 0, right: -150), size: .init(width: 120, height: 70))
nextExcersise.anchor(top: nil, leading: nextSet.trailingAnchor, bottom: nextSet.bottomAnchor, trailing: view.trailingAnchor, padding: .init(top: 0, left: 85, bottom: 0, right: -40), size: .init(width: 120, height: 70))
}
//MARK: - ImageView Constraints
func setImageViewConstraints(){
timerImage.anchor(top: repsTextField.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 40, left: 0, bottom: 0, right: 0), size: .init(width: 100, height: 100))
}
//MARK: - TextView Constraints
func setTextViewConstraints(){
notesTextView.anchor(top: timerImage.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 40, left: 40, bottom: 0, right: -40), size: .init(width: 100, height: 110))
}
//MARK: - Navigation Bar Setup
func navConAcc(){
navigationItem.title = selectedExercise?.exerciseName
navigationController?.navigationBar.prefersLargeTitles = true
}
//MARK: - Stopwatch
func timeClock(){
let image1 = UIImage(named: "stopwatch")
timerImage = UIImageView(image: image1)
timerImage.contentMode = .scaleAspectFit
self.view.addSubview(timerImage)
}
//MARK: - Load Data
func loadWsr() {
stats = selectedExercise?.wsr.sorted(byKeyPath: "sets", ascending: true)
}
//MARK: - Save Data
func save(wsr : WeightSetsReps){
do {
try realm.write {
realm.add(wsr)
}
} catch {
print("Error saving wsr data \(error)")
}
}
}
extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero){
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
}
if let leading = leading {
leadingAnchor.constraint(equalTo: leading, constant: padding.left).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom, constant: padding.bottom).isActive = true
}
if let trailing = trailing {
trailingAnchor.constraint(equalTo: trailing, constant: padding.right).isActive = true
}
if size.width != 0 {
widthAnchor.constraint(equalToConstant: size.width).isActive = true
}
if size.height != 0 {
heightAnchor.constraint(equalToConstant: size.height).isActive = true
}
}
}
You're not implementing programmatic view controllers correctly. A programmatically-created view controller does all of its view building in loadView(), not viewDidLoad(). Therefore, add all of the view controller's subviews in loadView() (without calling super.loadView()). Then use viewDidLoad() (with calling super.viewDidLoad()) to do post-view work, like adding timers, notification observers, etc. I suspect the lagging is caused by an incorrectly-configured lifecycle.
It also appears you're relatively new to iOS or Swift development and so I would strongly suggest you do not use extensions, especially on UIView for auto layout. Learn how it all works first before you begin extending things. The process for programmatic auto layout is:
// adjust parameters first, like color, delegate, etc.
someView.translatesAutoresizingMaskIntoConstraints = false // set resizing to false before adding as a subview
view.addSubview(someView) // add as a subview
someView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true // add constraints

Unable to activate constraint with anchors while adding a textfield

A while ago I started creating my screens via the storyboard.
Recently I have been convinced doing it programmatically.
Currently I am running into the following error:
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors and because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
import UIKit
class ViewController: UIViewController {
var emailTextField: UITextField {
let tf = UITextField()
tf.placeholder = "email";
tf.backgroundColor = UIColor(white: 0, alpha: 0.03)
tf.borderStyle = .roundedRect
tf.font = UIFont.systemFont(ofSize: 14)
return tf
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(emailTextField)
emailTextField.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 40, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 40)
}
}
I have created a function to anchor my elements in Extensions.swift:
import UIKit
extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat) {
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
self.topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
}
if let left = left {
self.leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
}
if let bottom = bottom {
self.bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
}
if let right = right {
self.rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
}
if width != 0 {
widthAnchor.constraint(equalToConstant: width).isActive = true
}
if height != 0 {
heightAnchor.constraint(equalToConstant: height).isActive = true
}
}
}
In addition, I deleted the Application Scene Manifest in Info.plist.
I want my project to support the upcoming iOS versions starting at 13.0.
Nevertheless I don't want to use the new framework SwiftUI.
Are there any specific things to watch out for (e.g. Scene Delegate)?
Thank you in advance!
You need to change this
var emailTextField: UITextField {
let tf = UITextField()
tf.placeholder = "email";
tf.backgroundColor = UIColor(white: 0, alpha: 0.03)
tf.borderStyle = .roundedRect
tf.font = UIFont.systemFont(ofSize: 14)
return tf
}
into this
var emailTextField: UITextField = {
let tf = UITextField()
tf.placeholder = "email";
tf.backgroundColor = UIColor(white: 0, alpha: 0.03)
tf.borderStyle = .roundedRect
tf.font = UIFont.systemFont(ofSize: 14)
return tf
}()
In current version emailTextField is computed property, so, every time you access it compiler create a new instance (it works like function, not variable).
Textfield was unable to get its parent view while you were adding its constraint. Below code works for me. I passed current view as a param to anchor() method and add emailTextField as a subview there.
class ViewController: UIViewController {
var emailTextField:UITextField = {
let tf = UITextField()
tf.placeholder = "email";
tf.backgroundColor = UIColor(white: 0, alpha: 0.03)
tf.borderStyle = .roundedRect
tf.font = UIFont.systemFont(ofSize: 14)
return tf
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
emailTextField.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 40, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 40, view:view)
}
}
extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat, view:UIView) {
translatesAutoresizingMaskIntoConstraints = false
view.addSubview(self)
if let top = top {
self.topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
}
if let left = left {
self.leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
}
if let bottom = bottom {
self.bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
}
if let right = right {
self.rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
}
if width != 0 {
widthAnchor.constraint(equalToConstant: width).isActive = true
}
if height != 0 {
heightAnchor.constraint(equalToConstant: height).isActive = true
}
}
}

Having issues with activating constraints programmatically and getting errors

Last night I posted a question on here where I used storyboards to set the constraints. I decided to test it with anchors and do constraints programmatically.
I am getting this error:
Terminating app due to uncaught exception 'NSGenericException',
reason: 'Unable to activate constraint with anchors
and
because they have no common ancestor. Does the constraint or its
anchors reference items in different view hierarchies? That's
illegal.'
I know this has to do with views not being a subview of a certain other view however, I have checked it plenty of times and I don't know what to do.
here is the code:
import UIKit
import CoreData
class editViewController: UIViewController {
let screenSize: CGRect = UIScreen.main.bounds
let moContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var editnotes: addednotes?
let mainbackgroundview: UIView =
{
let view = UIView()
view.backgroundColor = .black
view.alpha = 0.3
return view
}()
let backgroundview1: UIView =
{
let view = UIView()
view.backgroundColor = .purple
return view
}()
let edittasktextview1: UITextView = {
let tv = UITextView()
tv.backgroundColor = .black
return tv
}()
let cancelButton : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Cancel", for: .normal)
button.addTarget(self, action: #selector(handleCancelButton), for:
.touchUpInside)
return button
}()
let doneButton1 : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Done", for: .normal)
button.addTarget(self, action: #selector(handleDoneButton), for: .touchUpInside)
return button
}()
#objc func handleCancelButton() {
edittasktextview1.endEditing(true)
dismiss(animated: true, completion: nil)
}
#objc func handleDoneButton()
{
dismiss(animated: true, completion: nil)
if edittasktextview1.text.isEmpty == true
{
moContext.delete(editnotes!)
}
else
{
editnotes?.sNote = edittasktextview1.text
}
var error: NSError?
do {
// Save The object
try moContext.save()
print("SAVED")
} catch let error1 as NSError {
error = error1
}
edittasktextview1.endEditing(true)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NotificationID"), object: nil)
}
override func viewDidAppear(_ animated: Bool) {
//add something here
// edittasktextview.becomeFirstResponder()
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.tintColor = UIColor.white
navigationItem.title = "Edit Tasks"
edittasktextview1.text = editnotes?.sNote
backgroundview1.backgroundColor = editnotes?.sPriorityColor
edittasktextview1.backgroundColor = .clear
setupviews()
//adding gesture to the background view to dismiss keyboard
let dismisskeyboardgesture = UITapGestureRecognizer()
dismisskeyboardgesture.addTarget(self, action: #selector(dismisskeyboard))
let maskPath = UIBezierPath.init(roundedRect: self.backgroundview1.bounds, byRoundingCorners:[.topLeft, .topRight], cornerRadii: CGSize.init(width: 20.0, height: 0))
let maskLayer = CAShapeLayer()
maskLayer.frame = self.backgroundview1.bounds
maskLayer.path = maskPath.cgPath
self.backgroundview1.layer.mask = maskLayer
view.backgroundColor = .clear
//view.addGestureRecognizer(dismisskeyboardgesture)
view.addSubview(mainbackgroundview)
mainbackgroundview.addSubview(backgroundview1)
// view.addSubview(backgroundview1)
backgroundview1.addSubview(edittasktextview1)
backgroundview1.addSubview(cancelButton)
backgroundview1.addSubview(doneButton1)
}
#objc func dismisskeyboard()
{
edittasktextview1.endEditing(true)
}
func setupviews()
{
mainbackgroundview.anchorToTop(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
backgroundview1.anchor(view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, topConstant: 45, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 0)
edittasktextview1.anchor(view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, topConstant: 40, leftConstant: 8, bottomConstant: 0, rightConstant: 8, widthConstant: 0, heightConstant: 150)
cancelButton.anchor(edittasktextview1.bottomAnchor, left: nil, bottom: nil, right: view.rightAnchor, topConstant: 18, leftConstant: 0, bottomConstant: 0, rightConstant: 8, widthConstant: 40, heightConstant: 30)
doneButton1.anchor(edittasktextview1.bottomAnchor, left: view.leftAnchor, bottom: nil, right: nil, topConstant: 18, leftConstant: 8, bottomConstant: 0, rightConstant: 0, widthConstant: 40, heightConstant: 30)
}
}
Help will be appreciated, as I am stuck at this problem for nearly 18 hours :(
UPDATE #1
Not getting the same error now, however, I am not able to set the backgroundview1 on the mainbackgroundview.
I am adding it as a subview:
mainbackgroundview.addSubview(backgroundview1)
In setupviews() I am placing the constraint like so:
NSLayoutConstraint.activate([
backgroundview1.leftAnchor.constraint(equalTo: mainbackgroundview.leftAnchor, constant: 0),
backgroundview1.rightAnchor.constraint(equalTo: mainbackgroundview.rightAnchor, constant: 0),
backgroundview1.bottomAnchor.constraint(equalTo: mainbackgroundview.bottomAnchor, constant: 0),
backgroundview1.topAnchor.constraint(equalTo: mainbackgroundview.topAnchor, constant: 45)
])
Still I am not getting the backgroundview1 to be placed on the mainbackgroundview. The mainbackgroundview is showing up though on the simulator.
I am adding mainbackgroundview like so:
NSLayoutConstraint.activate([
mainbackgroundview.leftAnchor.constraint(equalTo: view.leftAnchor),
mainbackgroundview.rightAnchor.constraint(equalTo: view.rightAnchor),
mainbackgroundview.topAnchor.constraint(equalTo: view.topAnchor),
mainbackgroundview.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
What am I doing wrong now?
In setupViews() function you are adding constraints but not setting them as active. This is a sample anchor constraint that can be added successfully:
myView.bottomAnchor.constraint(equalTo: view.topAnchor,
constant: 8).isActive=true
Try adding .isActive at the end of each of your constraint.
Moreover you can add relevant anchor constraints for example: topAnchor, bottomAnchor, widthAnchor, heightAnchor, leadingAnchor etc. Try simplifying your constraints.

Swift: UIScrollView won't work with constraints at all?

I'm trying to use a UIScrollView on my view as the content that I want to show won't fit on the screen. I work purely programmatically and cannot seem to grasp why my UIScrollView simply will not work when adding constraints. It works perfectly with frames but I that's the awkward, old way of doing things. I want to be able to add views to the scroll view and of course scroll down as it goes off screen. I have tried to add a topView as one of the views I want to be able to add and scroll with the scrollview but no joy. Where am I going wrong? Any help will be highly appreciated.
class ScrollTest: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupviews()
}
let topView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
return view
}()
let containerView: UIView = {
let view = UIView()
view.backgroundColor = .white
return view
}()
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.backgroundColor = .white
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
func setupviews() {
view.addSubview(scrollView)
scrollView.addSubview(containerView)
containerView.addSubview(topView)
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
//
_ = containerView.anchor(scrollView.topAnchor, left: scrollView.leftAnchor, bottom: scrollView.bottomAnchor, right: scrollView.rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 700)
_ = topView.anchor(view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, topConstant: 100, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 50)
}
}
Couple of things. First, your containerView is missing translatesAutoresizingMaskIntoConstraints = false. Second, mostly, an UI objects needs four constraints to be valid. So you are missing some of them for your container view.
Here is a complete working code
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.backgroundColor = .red
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
let containerView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
scrollView.contentSize = CGSize(width: 320, height: 1500)
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.addSubview(containerView)
containerView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
containerView.widthAnchor.constraint(equalToConstant: 200).isActive = true
containerView.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
Results is like

Resources