Disabling uicollectionview scroll makes uicollectionview smaller - ios

I have got this uicollectionview
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 90
collectionView = UICollectionView(frame: CGRect(x:0,y:0,width:0,height:0), collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.isScrollEnabled = false
collectionView.heightAnchor.constraint(equalToConstant: collectionView.contentSize.height).isActive = true collectionView.topAnchor.constraint(equalTo:PostsTab.bottomAnchor).isActive = true
collectionView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
collectionView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
Now this collectionView is inside scrollView.On top of collectionview there is another view and it all looks like this
Now when i scroll down i want that UIView that is on top of collectionView to be left behind that's why i added isScrollEnabled = false,without that when i scroll down uiview stays on a fixed position
like this
override func viewDidAppear(_ animated: Bool) {
scrollView.contentSize = CGSize(width:1,height: 2400)
collectionView.heightAnchor.constraint(equalToConstant: collectionView.contentSize.height).isActive = true
}

Related

dynamic height of UICollectionView (horizontal scrolling) based on cell height

I've got the following situation:
A collectionView inside a containerView that sits below the navigationBar. The view is set up programmatically (no xib).
I want to place the containerView in a way that it does not need a height constraint. Is that possible? The containerView should get it's height based on the constraints in the collectionView cells.
Code:
This is the function which lays out the containerView inside the viewController:
private func setUpContainerView() {
view.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
containerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20)
])
}
Inside the containerView (which is a subclass of UIView), the collectionView is set up like that (called in the init of that class):
private func setUpCollectionView() {
collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: collectionViewFlowLayout())
guard let collectionView = collectionView else { return }
addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
collectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
collectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
collectionView.topAnchor.constraint(equalTo: self.topAnchor)
])
collectionView.delegate = self
collectionView.dataSource = self
registerCollectionViewNibs()
}
private func collectionViewFlowLayout() -> UICollectionViewFlowLayout{
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 10
flowLayout.minimumLineSpacing = 10
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
return flowLayout
}
When I do it like that, the collectionView is not visible. It only shows up when I add a height constraint with a specific constant to the containerView (inside the setUpContainerView function).
The problem is, that I don't know that height. I would have to set that height to 10+10+50 = 70 manually. But I want to make the containerView get it's size based on the collectionView cell constraints. Is that possible?

How do I align ImageView to the bottom of ScrollView programmatically?

I am trying to align a background image to the bottom of a scroll view that fits the screen, programmatically using Autolayout. Ideally, I want the image to be always aligned at the bottom of the scroll view. When the content of the scroll view goes beyond the screen height or when scroll view content size is less than screen height with scroll view fitting the whole screen.
MyView
class MyView: UIView {
let myScrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.bounces = false
return scrollView
}()
let contentView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let myLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello world"
label.font = UIFont.systemFont(ofSize: 24)
return label
}()
let myImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = #imageLiteral(resourceName: "Mask Group 3")
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
backgroundColor = .white
addSubview(myScrollView)
myScrollView.addSubview(contentView)
contentView.addSubview(myLabel)
contentView.addSubview(myImageView)
}
private func setupConstraints() {
myScrollView.topAnchor.constraint(equalTo: topAnchor).isActive = true
myScrollView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
myScrollView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
myScrollView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: myScrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: myScrollView.bottomAnchor).isActive = true
contentView.leftAnchor.constraint(equalTo: myScrollView.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: myScrollView.rightAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
// If I am setting this and when the content size go beyond the screen, it does not scroll
// If I don't set this, there is no content size and image view will not position correctly
// contentView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
myLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 200).isActive = true
myLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
myImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
myImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
myImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
}
}
MyViewController
import UIKit
class MyViewController: UIViewController {
override func loadView() {
view = MyView()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: false)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Illustration
I have found the solution.
A contentView is needed.
Set the contentView's top, left, bottom, right constraint equal to scrollView edges.
Set the contentView's width equal to view's width anchor
Set the contentView's height greaterThanOrEqualTo view's height anchor
Set the imageView's bottom equal to the bottom anchor of contentView.
For the imageView's top, set the constraint to an element with greaterThanOrEqualTo, to give it a constant gap and avoid overlapping of elements in smaller screens.
It is seems ok:
private func setupConstraints() {
myScrollView.topAnchor.constraint(equalTo: topAnchor).isActive = true
myScrollView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
myScrollView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
myScrollView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: myScrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: myScrollView.bottomAnchor).isActive = true
contentView.leftAnchor.constraint(equalTo: myScrollView.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: myScrollView.rightAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
// If I am setting this and when the content size go beyond the screen, it does not scroll
// If I don't set this, there is no content size and image view will not position correctly
contentView.heightAnchor.constraint(equalToConstant: 1400).isActive = true
myLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 200).isActive = true
myLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
myImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
myImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
myImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
myImageView.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
If think you just forgot to specify image height:
myImageView.heightAnchor.constraint(equalToConstant: 200).isActive = true

Minimum height for UICollectionView (Swift)

Im trying to create a simple UICollectionView in Swift with a custom UICollectionViewCell. I have the following method in my UICollectionViewController to set the size of a cell;
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (collectionView.frame.width / 3) - 10
let height = CGFloat(170)
return CGSize(width: width, height: height)
}
I then set constraints for the cell as follows;
private func setImageViewConstraints() {
itemImageView.translatesAutoresizingMaskIntoConstraints = false // enables Auto-Layout
itemImageView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true // center horizontally
itemImageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true // 8pt from top of cell
// 1:1 Aspect Ratio
itemImageView.widthAnchor.constraint(equalToConstant: (self.frame.width - 4)).isActive = true
itemImageView.heightAnchor.constraint(equalTo: itemImageView.widthAnchor).isActive = true
}
private func setupLabelConstraints() {
itemLabel.translatesAutoresizingMaskIntoConstraints = false
itemLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
itemLabel.leftAnchor.constraint(equalTo: itemImageView.leftAnchor, constant: 0).isActive = true
itemLabel.rightAnchor.constraint(equalTo: itemImageView.rightAnchor, constant: 0).isActive = true
itemLabel.topAnchor.constraint(equalTo: itemImageView.bottomAnchor, constant: 4).isActive = true
itemLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -4).isActive = true
}
However, the height of the cell varies, depending on the height of it's content, for example;
How can I set the height of the cells so they are all the same?
Thanks!

Auto Layout using layout anchors programmatically not working

I am trying to add a stack view containing a UILabel at the top and a UICollectionView underneath. I am trying to constrain the stack view so that it takes up the full view, by anchoring it to all sides. When I run the app the UILabel appears and a slice of the collection view appears. The collection view says its width and height are both zero. Any help would be appreciated, thank you.
var collectionView: UICollectionView!
var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.translatesAutoresizingMaskIntoConstraints = false
let margins = self.view.layoutMarginsGuide
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
layout.itemSize = CGSize(width: self.collectionView.frame.width / 4, height: self.collectionView.frame.width / 4)
layout.minimumInteritemSpacing = self.collectionView.frame.width / 15
layout.minimumLineSpacing = self.collectionView.frame.width / 5
collectionView.backgroundColor = UIColor.black
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
titleLabel = UILabel()
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 1
titleLabel.font = UIFont.systemFont(ofSize: 22)
titleLabel.backgroundColor = UIColor.lightGray
titleLabel.text = "Challenges"
titleLabel.textColor = UIColor.red
let stackView = UIStackView(arrangedSubviews: [titleLabel, collectionView])
stackView.backgroundColor = UIColor.white
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 5
self.view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
//stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
//stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 1.0).isActive = true
}
UPDATE:
I took my code and ran it in a new single view application and it worked as expected. Because of this I think it is worth it to mention that I am trying to incorporate this into a sprite kit game and I present the view controller by doing this:
let root = self.view?.window?.rootViewController
var viewC = SelectionCollectionViewController()
root?.present(viewC, animated: false, completion: nil)
Are there special steps I need to take because this is done with sprite kit?
I believe below screen shot is the expected output.
You might not need UIStackView , You can directly add it to self.view.
Once added to self.view, you can set up constraints. You can print the item Size in viewDidLayoutSubviews
class ViewController: UIViewController {
var collectionView: UICollectionView!
var titleLabel: UILabel!
let collectionCellIdentifier:String = "collectionCellId"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.classForCoder(), forCellWithReuseIdentifier: collectionCellIdentifier)
collectionView.backgroundColor = UIColor.black
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
titleLabel = UILabel()
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 1
titleLabel.font = UIFont.systemFont(ofSize: 22)
titleLabel.backgroundColor = UIColor.lightGray
titleLabel.text = "Challenges"
titleLabel.textColor = UIColor.red
titleLabel.translatesAutoresizingMaskIntoConstraints = false
collectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(titleLabel)
self.view.addSubview(collectionView)
setUpConstraints()
}
func setUpConstraints(){
self.view.addConstraint(NSLayoutConstraint(item: self.titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier:0, constant:50.0 ))
titleLabel.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
titleLabel.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let flowLayout = (collectionView.collectionViewLayout as! UICollectionViewFlowLayout)
flowLayout.itemSize = CGSize(width: collectionView.frame.width / 4.0 , height: collectionView.frame.width / 4.0)
flowLayout.minimumInteritemSpacing = self.collectionView.frame.width / 15.0
flowLayout.minimumLineSpacing = self.collectionView.frame.width / 5.0
}
}
extension ViewController:UICollectionViewDataSource,UICollectionViewDelegate {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionCellIdentifier, for: indexPath)
cell.backgroundColor = UIColor.gray
return cell
}
}
I think you need to know what happen in these functions:
viewDidLoad
viewWillAppear
viewWillLayoutSubviews
viewDidLayoutSubviews
viewDidAppear
if you use autoLayout, when in viewDidLoad(), the frame is not confirmed, because the view will make an auto adjustment when in viewWillLayoutSubviews() and viewDidLayoutSubviews(), so I suggest you make these code in viewDidAppear(), and then you may see what you want!
Furthermore, if you use storyboard or nib, you need to do these in awakeFromNib()

Swift: is it okay to add constraints to UICollectionview?

I have this class
class HomePage: UIViewController,UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate
And in this class i have a lot of UIViews.One of which is tabView.Now i want to add my collectionView to the bottom of tabView,how can i do that? Here is my `collectionView
var flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: CGRect(x:0,y:500,width:self.view.frame.size.width,height:100 ), collectionViewLayout: flowLayout)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = .red`
And constraints to tabView
tabView.topAnchor.constraint(equalTo: profileInfWrapper.bottomAnchor).isActive = true
tabView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tabView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width/4).isActive = true
tabView.heightAnchor.constraint(equalToConstant: 80).isActive = true
You can add the collectionView inside tabView and setup constraints:
collectionView.translatesAutoresizingMaskIntoConstraints = false
tabView.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: tabView.topAnchor, constant: 20).isActive = true
.... And add other constraints
Or you can add it under tab view like so:
self.view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: tabView.bottomAnchor, constant: 20).isActive = true
__
BTW you can set tabView's width like so:
tabView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.25).isActive = true

Resources