I made this app like that above image. but Scroll is not working. I want make scrollView fully down.
I have asked a question related of this and I was worked. But I need make some function, and Strategy is...
NSLayoutConstraint.activate([...topAnchor...(equalTo: aboveView.bottomAnchor, constrant: 20)
Code is down Bellow. All Views are setting by code.
DetailViewController.Swift
import UIKit
class DetailViewController: UIViewController {
private let titleLabel: UILabel = {
let view = UILabel()
view.numberOfLines = 2
return view
}()
private let dateLabel: UILabel = {
let view = UILabel()
return view
}()
private let scroll: UIScrollView = {
let scroll = UIScrollView()
scroll.backgroundColor = .brown
return scroll
}()
private let container: UIView = {
let container = UIView()
container.backgroundColor = .lightGray
return container
}()
private let image: UIImageView = {
let image = UIImageView()
return image
}()
private let controller: UIPageControl = {
let controller = UIPageControl()
return controller
}()
private let content: UITextView = {
let content = UITextView()
return content
}()
var fileName: String!
var id: String!
var entryNumber: Int!
var dataSize: Int!
var entry: Entry!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Get contents id and parse file name
parsingFileName(id: id)
addScrollView()
scroll.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height)
addTopLabels()
content.text = entry. Article
// isImage is true or false
addContent(isImage: entry.isImage)
// For debugging
content.backgroundColor = .yellow
scroll.updateContentSize()
content.contentSize = CGSize(width: self.scroll.frame.width, height: self.scroll.frame.height)
}
}
Extension 1: Union Function
extension UIScrollView {
func updateContentSize() {
let unionTotal = recursiveUnion(view: self)
self.contentSize = CGSize(width: self.frame.width, height: unionTotal.height + 50)
}
private func recursiveUnion(view: UIView) -> CGRect {
var totalRect: CGRect = .zero
for v in view.subviews {
totalRect = totalRect.union(recursiveUnion(view: v))
}
print("Updated content Size: \(totalRect.union(view.frame))")
return totalRect.union(view.frame)
}
}
Extension 2: Set views and constraints
extension DetailViewController {
fileprivate func parsingFileName(id: String) {
// parse ID and get filename
// ref.: https://ssooyn.tistory.com/22
let s = id.index(id.startIndex, offsetBy: 0)
let e = id.index(id.startIndex, offsetBy: 7)
let r = s...e
fileName = "entry-" + String(id[r])
let dataFromFile = DataLoader(fileName: fileName, fileType: ".json").entry
// Find the entry
for i in 0 ..< dataFromFile.count {
if dataFromFile[i].id == id {
entry = dataFromFile[i]
break
}
}
}
fileprivate func addScrollView() {
// Setting views - ScrollView and Container
view.addSubview(scroll)
scroll.translatesAutoresizingMaskIntoConstraints = false
scroll.addSubview(container)
container.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scroll.topAnchor.constraint(equalTo: self.view.topAnchor),
scroll.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
scroll.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
scroll.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
container.topAnchor.constraint(equalTo: self.scroll.topAnchor),
container.bottomAnchor.constraint(equalTo: self.scroll.bottomAnchor),
container.leadingAnchor.constraint(equalTo: self.scroll.leadingAnchor),
container.trailingAnchor.constraint(equalTo: self.scroll.trailingAnchor),
container.widthAnchor.constraint(equalTo: self.scroll.widthAnchor, multiplier: 1),
container.heightAnchor.constraint(equalTo: self.scroll.heightAnchor, multiplier: 1)
])
}
fileprivate func addTopLabels() {
// Setting Views - Top Labels
titleLabel.textAlignment = .left
titleLabel.text = entry.title
titleLabel.font = UIFont(name: "Pretendard-SemiBold", size: 17)
dateLabel.textAlignment = .right
dateLabel.text = String(entry.year) + "." + String(entry.month) + "." + String(entry.day)
container.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(dateLabel)
dateLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: container.topAnchor),
titleLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor),
titleLabel.trailingAnchor.constraint(equalTo: container.trailingAnchor),
titleLabel.heightAnchor.constraint(equalToConstant: 20),
titleLabel.widthAnchor.constraint(equalTo: container.widthAnchor),
dateLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor),
dateLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor),
dateLabel.trailingAnchor.constraint(equalTo: container.trailingAnchor),
dateLabel.heightAnchor.constraint(equalToConstant: 20),
dateLabel.widthAnchor.constraint(equalTo: container.widthAnchor),
])
}
fileprivate func addContent(isImage: Bool) {
if isImage == false {
print("No Image")
image.backgroundColor = .darkGray
image.isHidden = true
container.addSubview(content)
content.translatesAutoresizingMaskIntoConstraints = false
content.sizeToFit()
content.isScrollEnabled = false
NSLayoutConstraint.activate([
content.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: 20),
content.leadingAnchor.constraint(equalTo: container.leadingAnchor),
content.trailingAnchor.constraint(equalTo: container.trailingAnchor),
// content.bottomAnchor.constraint(equalTo: container.bottomAnchor),
content.heightAnchor.constraint(equalTo: container.heightAnchor),
content.widthAnchor.constraint(equalTo: container.widthAnchor),
])
} else {
print("There a image")
image.backgroundColor = .red
image.image = UIImage(named: "202310172204-1-1.jpeg")
container.addSubview(image)
content.translatesAutoresizingMaskIntoConstraints = false
content.sizeToFit()
content.isScrollEnabled = false
container.addSubview(content)
image.translatesAutoresizingMaskIntoConstraints = false
print(content.contentSize.height)
NSLayoutConstraint.activate([
image.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: +20),
image.leadingAnchor.constraint(equalTo: container.leadingAnchor),
image.trailingAnchor.constraint(equalTo: container.trailingAnchor),
image.heightAnchor.constraint(equalTo: container.widthAnchor),
image.widthAnchor.constraint(equalTo: container.widthAnchor),
content.topAnchor.constraint(equalTo: image.bottomAnchor, constant: +20),
content.leadingAnchor.constraint(equalTo: container.leadingAnchor),
content.trailingAnchor.constraint(equalTo: container.trailingAnchor),
// content.bottomAnchor.constraint(equalTo: container.bottomAnchor),
content.heightAnchor.constraint(equalTo: container.heightAnchor),
content.widthAnchor.constraint(equalTo: container.widthAnchor),
])
}
}
}
Thank you for reading long story and code. :) Have a nice day!
First, you are doing a whole lot of unnecessary stuff...
Second, get in the practice of naming properties / objects / etc in a "readable" way, e.g. content
doesn't tell you (or someone else looking at your code) what it is. Using contentTextView
is obvious.
Third, start simple - especially while you're learning.
Take a look at this code:
class DetailViewController: UIViewController {
private let titleLabel: UILabel = {
let view = UILabel()
view.numberOfLines = 2
return view
}()
private let dateLabel: UILabel = {
let view = UILabel()
return view
}()
private let scrollView: UIScrollView = {
let scroll = UIScrollView()
scroll.backgroundColor = .brown
return scroll
}()
private let topImageView: UIImageView = {
let image = UIImageView()
return image
}()
private let contentTextView: UITextView = {
let content = UITextView()
content.isScrollEnabled = false
return content
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
titleLabel.text = "This is the Title Label"
//image.image = UIImage(named: "202310172204-1-1.jpeg")
if let img = UIImage(systemName: "swift") {
topImageView.image = img
}
dateLabel.text = "This is the Date Label"
// let's create a string of 50 lines for the "content" text field
var sTemp: String = "Line 1"
for i in 2...50 {
sTemp += "\nLine \(i)"
}
contentTextView.text = sTemp
// let's put all those elements into a vertical UIStackView
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 8
[titleLabel, topImageView, dateLabel, contentTextView].forEach { v in
stackView.addArrangedSubview(v)
}
// top image view needs a height constraint
topImageView.heightAnchor.constraint(equalToConstant: 160.0).isActive = true
// add the stack view to the scroll view
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
// add the scroll view to self's view
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
let g = view.safeAreaLayoutGuide
let cg = scrollView.contentLayoutGuide
let fg = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
// constrain all 4 sides of scroll view
// we'll inset by 20-points so we can see the framing
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
// constrain all 4 sides of stack view
// to the scroll view's Content Layout Guide
// we'll inset by 12-points so we can see the framing
stackView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 12.0),
stackView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 12.0),
stackView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -12.0),
stackView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: -12.0),
// we'll make the stack view width 24-points less than
// the scroll view's Frame Layout Guide
// (24 because we have 12-points on each side)
stackView.widthAnchor.constraint(equalTo: fg.widthAnchor, constant: -24.0),
])
// let's set some background colors so we can see the framing
titleLabel.backgroundColor = .cyan
topImageView.backgroundColor = .red
dateLabel.backgroundColor = .green
contentTextView.backgroundColor = .yellow
// let's use a larger font for the content text view
// for demonstration purposes
contentTextView.font = .systemFont(ofSize: 20.0, weight: .light)
}
}
Here's the result:
Absolutely NO "calculating sizes" ... far, far fewer constraints needed ... everything scrolls as expected ... etc.