У меня возникла проблема с динамическим изменением размера прокрутки и прокруткой, чтобы она соответствовала всему моему контенту. Я делаю все это программно, так как считаю, что его проще всего использовать при работе с автоматической компоновкой. В любом случае, каждое решение, с которым я сталкивался в Интернете, похоже, не работает, и самое близкое, что я получил, было, когда я попробовал это. contentView.heightAnchor.constraint(equalTo: self.scrollView.frameLayoutGuide.heightAnchor).isActive = true
Установка привязки высоты контекстного представления к направляющей макета фрейма представления прокрутки позволила мне прокручивать, но она все еще не достигла дна. Я чувствую, что должен что-то упускать из виду, но я не могу понять это для своей жизни. Вот мой код ниже. Спасибо. Также извините за то, как он отображается ниже, я не могу заставить его все поместиться в поле.
Изменить: перед публикацией этого я экспериментировал с встраиванием моего представления контента в представление стека, поэтому оно там, но я пробовал без стека, и это все равно не сработало.
Изменить 2: найдено решение и исправлено отображение кода.
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)
}
}
Я нашел решение, которое сработало, и оказалось, что я пропустил что-то маленькое, что заблокировало меня на несколько часов. Я создал расширение UIScrollView и UIView с функцией, которая сортирует подпредставления по maxY каждого и изменяет размер представления и высоты содержимого, чтобы они были равны этому. Причина, по которой он не работал раньше, заключается в том, что представление прокрутки имело только одно подпредставление, которое было представлением-контейнером, в котором размещались все компоненты пользовательского интерфейса. Я точно не знаю, почему представление контейнера все еще не росло вместе с его подпредставлениями, потому что всякий раз, когда я избавлялся бы от каких-либо ограничений по высоте, оно оказывалось равным нулю, а подпредставления просто выходили бы за пределы, но с этим исправлением теперь работает нормально. Вот что я сделал и ссылка на то, где я нашел помощь .
Расширения
viewDidLayoutSubviews()
Вам не нужно вычислять высоту… и вам не нужно устанавливать
contentSize.height
.Дайте нижнему элементу нижнее ограничение.
В коде, который вы показали, добавьте эту строку в конце настройки ограничений:
После добавления этой строки избавьтесь от обоих этих расширений и избавьтесь от
viewDidLayoutSubviews()
— вы можете поместитьscrollView.delegate = self
вviewDidLoad()
.Я использовал некоторые постоянные строки, так как у меня нет вашего
код, но вот что я получаю БЕЗ этого ограничения:
и не может прокрутить.
Вот что я получаю С этим ограничением, и, как видите, теперь я могу прокручивать содержимое до конца: