collectionViewCells to collectionView position x:0, y:0 - swift

I am experiencing some layout issues with collectionViewCells. I am programmatically applying layout constraints and UICollectionViewCells are not being pinned to the 0,0 position in my collectionView. See attached screenshot for reference. Thanks in advance!
class CurrentUserPlaceDetailsVC: UIViewController, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
setupMenuBar()
}
let menuBar: MenuBar = {
let mb = MenuBar()
return mb
}()
private func setupMenuBar() {
view.addSubview(menuBar)
view.addConstraintsWithFormat("H:|[v0]|", views: menuBar)
view.addConstraintsWithFormat("V:|-64-[v0(150)]", views: menuBar)
}
var placesTableVC: PlacesTableVC?
}
class MenuBar: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.green
cv.dataSource = self
cv.delegate = self
return cv
}()
let cellId = "cellId"
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
addSubview(collectionView)
addConstraintsWithFormat("H:|[v0]|", views: collectionView)
addConstraintsWithFormat("V:|[v0]|", views: collectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = .blue
return cell
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension UIView {
func addConstraintsWithFormat(_ format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
view.translatesAutoresizingMaskIntoConstraints = false
viewsDictionary[key] = view
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}

You need to set the minimumLineSpacing, headerReferenceSize, and/or sectionInset of the UICollectionViewFlowLayout instance you use to initialize your UICollectionView. You can also set the contentInset of the UICollectionView.
Try this:
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.headerReferenceSize = .zero
layout.sectionInset = .zero
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .green
cv.dataSource = self
cv.delegate = self
cv.contentInset = .zero
return cv
}()

Related

Adding Leading and Trailing into swift image view

I'm want to add the leading and trailing into Imageview . The Imageview properties pass from collection view cell. I have tried following to add the leading and trailing but still the leading and trilling not added into Imageview .
//imageView.leftAnchor.constraint(equalTo: view.leftAnchor , constant: 10),
//imageView.rightAnchor.constraint(equalTo: view.rightAnchor,constant: 10),
imageView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.leadingAnchor, multiplier: 3),
imageView.trailingAnchor.constraint(equalToSystemSpacingAfter: view.trailingAnchor, multiplier: 10),
Here is the view controller with collection view cell:
class PhotoViewController: UIViewController {
var viewModel : PhotoViewModel
init(viewModel : PhotoViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
private let collectionView: UICollectionView = {
let viewLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout)
collectionView.backgroundColor = .white
return collectionView
}()
private enum LayoutConstant {
static let spacing: CGFloat = 16.0
static let itemHeight: CGFloat = 230.0
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private var activityIndicator: UIActivityIndicatorView = {
let activityIndicator = UIActivityIndicatorView()
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
return activityIndicator
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupLayouts()
viewModel.fecthPhotoRecord()
collectionView.delegate = self
collectionView.dataSource = self
viewModel.delegate = self
view.backgroundColor = .lightGray
collectionView.reloadData()
}
private func setupViews() {
view.backgroundColor = .white
view.addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: PhotoCollectionViewCell.identifier)
}
private func setupLayouts() {
collectionView.translatesAutoresizingMaskIntoConstraints = false
// Layout constraints for `collectionView`
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor),
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor)
])
}
}
extension PhotoViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewModel.hits.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.identifier, for: indexPath) as! PhotoCollectionViewCell
let row = indexPath.row
let hits = viewModel.hits[indexPath.row]
cell.configureCell(tagName: hits.tags)
cell.configureImageCell(row: row, viewModel: viewModel)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
let detailVC = PhtotoDetailsViewController()
detailVC.rowSelected = indexPath.row
let navController = UINavigationController(rootViewController: detailVC)
detailVC.photoviewModel = viewModel
// navController.modalPresentationStyle = .fullScreen
present(navController, animated: true, completion: nil)
}
}
extension PhotoViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = itemWidth(for: view.frame.width, spacing: LayoutConstant.spacing)
return CGSize(width: width, height: LayoutConstant.itemHeight)
}
func itemWidth(for width: CGFloat, spacing: CGFloat) -> CGFloat {
let itemsInRow: CGFloat = 2
let totalSpacing: CGFloat = 2 * spacing + (itemsInRow - 1) * spacing
let finalWidth = (width - totalSpacing) / itemsInRow
return floor(finalWidth)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: LayoutConstant.spacing, left: LayoutConstant.spacing, bottom: LayoutConstant.spacing, right: LayoutConstant.spacing)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return LayoutConstant.spacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return LayoutConstant.spacing
}
}
Code in collection view cell .
import UIKit
protocol ReusableView: AnyObject {
static var identifier: String { get }
}
class PhotoCollectionViewCell: UICollectionViewCell {
private enum Constants {
// MARK: contentView layout constants
static let contentViewCornerRadius: CGFloat = 4.0
// MARK: profileImageView layout constants
static let imageHeight: CGFloat = 180.0
// MARK: Generic layout constants
static let verticalSpacing: CGFloat = 8.0
static let horizontalPadding: CGFloat = 16.0
static let profileDescriptionVerticalPadding: CGFloat = 8.0
}
private let imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.contentMode = .scaleAspectFill
return imageView
}()
private let tagLabel: UILabel = {
let label = UILabel(frame: .zero)
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: .zero)
setupViews()
setupLayouts()
}
private func setupViews() {
contentView.clipsToBounds = true
contentView.layer.cornerRadius = Constants.contentViewCornerRadius
contentView.backgroundColor = .white
contentView.addSubview(imageView)
contentView.addSubview(tagLabel)
}
private func setupLayouts() {
imageView.translatesAutoresizingMaskIntoConstraints = false
tagLabel.translatesAutoresizingMaskIntoConstraints = false
// Layout constraints for `imageView`
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
imageView.heightAnchor.constraint(equalToConstant: Constants.imageHeight)
])
// Layout constraints for `tagLabel`
NSLayoutConstraint.activate([
tagLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Constants.horizontalPadding),
tagLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Constants.horizontalPadding),
tagLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: Constants.profileDescriptionVerticalPadding)
])
}
func configureCell(tagName: String) {
tagLabel.text = tagName
}
func configureImageCell(row: Int, viewModel: PhotoViewModel) {
imageView.image = nil
viewModel
.downloadImage(row: row) { [weak self] data in
let image = UIImage(data: data)
self?.imageView.image = image
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension PhotoCollectionViewCell: ReusableView {
static var identifier: String {
return String(describing: self)
}
}
Here is the details view controller:
import UIKit
class PhtotoDetailsViewController: UIViewController {
var photoviewModel : PhotoViewModel?
var peopleDetailsViewModel:PeopleDetailsViewModel?
private let imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
private let tagLabel: UILabel = {
let label = UILabel(frame: .zero)
label.textAlignment = .center
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(imageView)
view.addSubview(tagLabel)
setUpUI()
setPhoto()
setContrain()
}
private func setContrain(){
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: view.topAnchor,constant: 100),
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: -200),
//imageView.leftAnchor.constraint(equalTo: view.leftAnchor , constant: 10),
//imageView.rightAnchor.constraint(equalTo: view.rightAnchor,constant: 10),
imageView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.leadingAnchor, multiplier: 3),
imageView.trailingAnchor.constraint(equalToSystemSpacingAfter: view.trailingAnchor, multiplier: 10),
tagLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor),
tagLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tagLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
private func setUpUI(){
tagLabel.text = photoviewModel?.hits.first?.tags
}
var rowSelected = 0
private func setPhoto(){
photoviewModel?.downloadImage(row: rowSelected) { [weak self] data in
DispatchQueue.main.async {
let image = UIImage(data: data)
self?.imageView.image = image
}
}
}
}
Debugging results in
Here is the screenshot:
Leading added .

Swift Add different actions to cells on a UICollectionView

I don't know where to start, I have a UICollectionView with multiple cells filled with a UIImage. I want each cell/UIImage to do a different action on a fingertap. Can someone pinpoint me in the right direction here?
The action that I have is a #IBAction from a UIButton, I know want have that action on a cell in the UICollectionView..
( I Guess I have to do something with the 'let cell = countryCollectionView.dequeueReusableCell' ?
import UIKit
class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
#IBOutlet weak var soundsCollectionView: UICollectionView!
lazy var cv: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
var cc = UICollectionView(frame: .zero, collectionViewLayout: layout)
cc.translatesAutoresizingMaskIntoConstraints = false
cc.register(CustomCell.self, forCellWithReuseIdentifier: "CustomCell")
cc.delegate = self
cc.dataSource = self
cc.backgroundColor = .white
return cc
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addSubview(cv)
cv.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
cv.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
cv.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
cv.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
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: "CustomCell", for: indexPath) as! CustomCell
cell.tag = indexPath.row
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 100)
}
}
class CustomCell: UICollectionViewCell {
lazy var centerImageView: UIImageView = {
var img = UIImageView()
img.translatesAutoresizingMaskIntoConstraints = false
img.image = UIImage(named: "1")
img.clipsToBounds = true
img.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handler(_:)))
tapGesture.numberOfTapsRequired = 1
img.addGestureRecognizer(tapGesture)
return img
}()
#objc private func handler(_ sender: UITapGestureRecognizer) {
print("tapped tag > ", self.tag)
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(centerImageView)
centerImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
centerImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
centerImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true
centerImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I edit as new programmatically example for your problem solution.
class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
lazy var cv: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
var cc = UICollectionView(frame: .zero, collectionViewLayout: layout)
cc.translatesAutoresizingMaskIntoConstraints = false
cc.register(CustomCell.self, forCellWithReuseIdentifier: "CustomCell")
cc.delegate = self
cc.dataSource = self
cc.backgroundColor = .white
return cc
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(cv)
cv.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
cv.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
cv.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
cv.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
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: "CustomCell", for: indexPath) as! CustomCell
cell.tag = indexPath.row
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 100)
}
}
class CustomCell: UICollectionViewCell {
lazy var centerImageView: UIImageView = {
var img = UIImageView()
img.translatesAutoresizingMaskIntoConstraints = false
img.image = UIImage(named: "1")
img.clipsToBounds = true
img.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handler(_:)))
tapGesture.numberOfTapsRequired = 1
img.addGestureRecognizer(tapGesture)
return img
}()
#objc private func handler(_ sender: UITapGestureRecognizer) {
print("tapped tag > ", self.tag)
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(centerImageView)
centerImageView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
centerImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
centerImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true
centerImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In this scenario, I would avoid using buttons on your cells. Instead you should hook into the didSelectItemAt delegate method and just add the necessary information into a data model:
struct Country {
let imageName: String
let sound: Sound // You didn't specify what type the sound1 object is but you get the gist
}
So your countries array will now contain this new struct instead of just raw Strings:
let countries = [
Country("country1", sound1),
Country("country2", sound2),
...
]
Then you can get the exact sound you want to play from the indexPath passed into didSelectItemAt:
let sound = self.countries[indexPath.row].sound
sound.play()
You'll also need to adjust how you're setting the cell's image in cellForItemAt:
let imageName = self.countries[indexPath.row].imageName
cell.countryImageView.image = UIImage(named: imageName)

Cells not appearing in UICollectionView

I don't understand why my cells aren't showing:
class MenuBar: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellId = "pouet"
lazy var customCollectionView : UICollectionView = {
let layout = UICollectionViewLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.gray
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
customCollectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
addSubview(customCollectionView)
addConstraintsWithFormat(format: "H:|[v0]|", views: customCollectionView)
addConstraintsWithFormat(format: "V:|[v0]|", views: customCollectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = customCollectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
print("test")
return cell
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now my print("test") is not showing either, meaning that the method is actually not called, right? So what is wrong ?
Change this line of code:
let layout = UICollectionViewLayout()
To:
let layout = UICollectionViewFlowLayout()
UICollectionViewLayout is abstract class you can't use it directly.

Swift 3: Be able to tap view behind a collectionViewCell?

I am working on a new feature in my app where I want a user to have an image and then be able to swipe through a collectionView on top of that image, adding "filters". The issue that I have is that I want the user to still be able to interact with the image, but it is behind the collectionView. If I set the collectionViews inUsersInteractionEnabled to false, of course I then cannot use the collectionView at all. I am going for a similar functionality to Snapchats "scroll through filters" feature. Any help will be highly appreciated. Thank you guys. Code is below:
import UIKit
class FirstCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() {
backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class SecondCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
let cellView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.alpha = 0.25
return view
}()
func setupViews() {
backgroundColor = .clear
addSubview(cellView)
cellView.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class SecondController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .clear
cv.dataSource = self
cv.delegate = self
cv.translatesAutoresizingMaskIntoConstraints = false
cv.isPagingEnabled = true
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(FirstCell.self, forCellWithReuseIdentifier: "firstCell")
collectionView.register(SecondCell.self, forCellWithReuseIdentifier: "secondCell")
view.backgroundColor = .white
setupViews()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "firstCell", for: indexPath) as! FirstCell
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "secondCell", for: indexPath) as! SecondCell
return cell
}
}
lazy var imageView: UIImageView = {
let iv = UIImageView()
let image = UIImage(named: "img")
iv.image = image
iv.contentMode = .scaleAspectFill
iv.translatesAutoresizingMaskIntoConstraints = false
iv.isUserInteractionEnabled = true
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
return iv
}()
func handleTap() {
print(123)
}
func setupViews() {
view.addSubview(imageView)
view.addSubview(collectionView)
imageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
imageView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
imageView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
}
}
There is a hitTest(_::) method available to override in any of the UIView subclass, which you can implement in your custom UICollectionView subclass and forward taps from it to any other view (the image behind collection view in your case).
override func hitTest(_ point: CGPoint,
with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if self.parentViewController.forwardTapToImageIfNeeded(point) {
return nil
}
return hitView
}
Where parentViewController is a reference to your SecondController instance, which should have forwardTapToImageIfNeeded(_) function to check if user's tap is on top of the image frame, and if it is — automatically pass tap event to it.
func forwardTapToImageIfNeeded(_ p: CGPoint) -> Bool {
/* Convert point to local coordinates */
let pointInSuperView = self.convert(p, from: collectionView)
/* Check if tap is on top of the image frame */
if imageBehindCollection.frame.contains(pointInSuperView) {
// Forward tap to the image `imageBehindCollection`
return true
}
return false
}
Note: I didn't test this particular code by myself, so it might need a few fixes in Xcode.

UICollectionView self sizing cells. Insert/reloadData breaks layout

Writing this question as my last resort, being stuck on this for few days now. I am trying to implement self sizing cells for UICollectionView with FlowLayout. Everything seem to work fine until I reladData() or do insertItemsAtIndexPaths() while being at the bottom of the collectionView.
This is my Cell:
class FooFoo: UICollectionViewCell {
var label: UILabel!
var text: String! {
didSet {
label.text = text
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
setupConstraints()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
self.contentView.backgroundColor = UIColor.yellowColor()
label = ({
let view = UILabel()
view.setTranslatesAutoresizingMaskIntoConstraints(false)
self.contentView.addSubview(view)
return view
})()
}
func setupConstraints() {
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-100-[childView]-100-|", options: nil, metrics: nil, views: ["childView": label]))
self.contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-100-[childView]-100-|", options: nil, metrics: nil, views: ["childView": label]))
}
}
This is CollectionView definition:
collectionView = ({
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 1
layout.estimatedItemSize = CGSize(width: 300, height: 300)
let view = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
view.dataSource = self
view.delegate = self
view.registerClass(FooFoo.self, forCellWithReuseIdentifier: "Cell")
view.backgroundColor = UIColor.clearColor()
self.view.addSubview(view)
return view
})()
This is dataSource:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! FooFoo
cell.text = "FooBar: \(indexPath.item)"
return cell
}
After I fetch additional items, i tried both inserting new array of indexes, or reloading data and the result is the same - broken layout.
I also tried replacing estimatedItemSize with itemSize and it did work just fine, so I am pretty certain its because self-sizing cells.
This is the result: