I am trying to set up images in the horizontal alignment in collectionView, but it's not working properly. Please check the attached screenshot.
View Controller Code:-
import UIKit
class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
@IBOutlet weak var texting1: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.texting1.register(UINib(nibName:"CollectionViewCell1", bundle: nil), forCellWithReuseIdentifier: "CollectionViewCell1")
self.texting1.delegate = self
self.texting1.dataSource = self
texting1.backgroundColor = .red
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell1", for: indexPath) as! CollectionViewCell1
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.view.frame.width, height: 120)
}
}
Output:- Image
CollectionViewCell1 Code:-
import UIKit
class CollectionViewCell1: UICollectionViewCell,UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
@IBOutlet weak var userImageColectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
self.userImageColectionView.register(UINib(nibName:"CollectionViewCell2", bundle: nil), forCellWithReuseIdentifier: "CollectionViewCell2")
userImageColectionView.delegate = self
userImageColectionView.dataSource = self
userImageColectionView.backgroundColor = .black
}
var items = ["1", "2", "3", "4"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell2", for: indexPath as IndexPath) as! CollectionViewCell2
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 70, height: 60);
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return -10
}
}
Xib ScreenShot:- Image
CollectionViewCell2 Code:-
class CollectionViewCell2: UICollectionViewCell {
@IBOutlet weak var imguserView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
imguserView.image = UIImage(systemName: "person.crop.circle")
}
}
Xib ScreenShot:- Image
My Goal:- Image
Can someone please explain to me how to show images under in collection view in the horizontal alignment and setup dynamic width according to cell images count, I've tried to implement by above but no results yet.
Any help would be greatly appreciated.
Thanks in advance.
One way to do this without using a collection view is to create a "horizontal chain" of constraints for the "cell views."
For example, if we have 3 image views - red, green, and blue - with these constraints:
NSLayoutConstraint.activate([
red.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
green.leadingAnchor.constraint(equalTo: red.trailingAnchor, constant: 10.0),
blue.leadingAnchor.constraint(equalTo: green.trailingAnchor, constant: 10.0),
])
it will look like this:
If we instead use negative constants:
NSLayoutConstraint.activate([
red.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
green.leadingAnchor.constraint(equalTo: red.trailingAnchor, constant: -20.0),
blue.leadingAnchor.constraint(equalTo: green.trailingAnchor, constant: -20.0),
])
it can look like this:
So we can create a custom UIView
subclass that handles all of that for us -- such as this:
class OverlapView: UIView {
public var overlap: CGFloat = -30 { didSet { setNeedsLayout() } }
public var cellViews: [UIView] = [] {
didSet {
// clear out any existing subviews
subviews.forEach { v in
v.removeFromSuperview()
}
cellViews.forEach { v in
// in case it wasn't set by the caller
v.translatesAutoresizingMaskIntoConstraints = false
addSubview(v)
// center it vertically
v.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
// we want the overlap to go from left-to-right
sendSubviewToBack(v)
}
setNeedsLayout()
}
}
private var horizontalConstraints: [NSLayoutConstraint] = []
override func layoutSubviews() {
super.layoutSubviews()
guard cellViews.count > 0 else { return }
// de-activate any existing horizontal constraints
NSLayoutConstraint.deactivate(horizontalConstraints)
var prevView: UIView!
cellViews.forEach { v in
// if it's the first one
if prevView == nil {
horizontalConstraints.append(v.leadingAnchor.constraint(equalTo: leadingAnchor))
} else {
horizontalConstraints.append(v.leadingAnchor.constraint(equalTo: prevView.trailingAnchor, constant: overlap))
}
prevView = v
}
// constrain last view's trailing
horizontalConstraints.append(prevView.trailingAnchor.constraint(equalTo: trailingAnchor))
// activate the udpated constraints
NSLayoutConstraint.activate(horizontalConstraints)
}
}
Here's a sample controller demonstrating that:
class OverlapTestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testView = OverlapView()
testView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(testView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
testView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
testView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
testView.heightAnchor.constraint(equalToConstant: 70.0),
// no trailing or width constraint
// width will be determined by the number of "cells"
])
// let's add 6 "cells" -- one for each color
let colors: [UIColor] = [
.systemRed, .systemGreen, .systemBlue,
.systemYellow, .systemCyan, .magenta,
]
var cellViews: [UIView] = []
colors.forEach { c in
if let img = UIImage(systemName: "person.crop.circle") {
let imgView = UIImageView(image: img)
imgView.tintColor = c
imgView.backgroundColor = .white
imgView.translatesAutoresizingMaskIntoConstraints = false
// let's use a 60x60 image view
imgView.widthAnchor.constraint(equalToConstant: 60.0).isActive = true
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor).isActive = true
// we want "round" views
imgView.layer.cornerRadius = 30.0
imgView.layer.masksToBounds = true
cellViews.append(imgView)
}
}
testView.cellViews = cellViews
// we can change the overlap value here if we don't want to use
// our custom view's default
// for example:
//testView.overlap = -40.0
// so we can see the framing
testView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
}
}
Output looks like this:
(We gave the custom view itself a light gray background so we can see its frame.)