I need to reload a background image in the MainVC when the user chooses it from the ViewController that will be dismissed but I can't figure out how. Any idea?
Ps. In the MainVC I have a collectionView, every cell contain another collectionView and these collectionViews have a custom cell.
When the user chooses a background image that image is passed to the custom cell and should be set as background.
Protocol
protocol ThemeDelegate {
func handlePassThemeData(data: UIImage)
}
MainVC
class MainVC: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, ThemeDelegate {
var currentTheme = UIImage(named: "4")!
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.register(QuoteCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView.register(FeaturedCell.self, forCellWithReuseIdentifier: featuredReuseIdentifier)
collectionView.register(AuthorCell.self, forCellWithReuseIdentifier: authorReuseIdentifier)
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad
setUpViews()
setupNavigationBarItems()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! QuoteCell
createGradientLayer(bounds: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height), cell: cell)
cell.delegate = self
cell.imageToPass = currentTheme
return cell
} else if indexPath.item == 1 {
...
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0 {
return CGSize(width: view.frame.width, height: view.frame.height / 1.5)
} else if indexPath.item == 1 {
return CGSize(width: view.frame.width, height: view.frame.height / 1.95)
} else {
return CGSize(width: view.frame.width, height: 160)
}
}
func setupNavigationBarItems() {
let themesButton = setUpBarButton(image: #imageLiteral(resourceName: "themes_icon_color"))
themesButton.addTarget(self, action: #selector(handleThemesTapped), for: .touchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: themesButton)
}
func setUpViews() {
view.addSubview(collectionView)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.anchors(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
}
func handlePassThemeData(data: UIImage) {
self.currentTheme = data
self.collectionView.reloadData()
}
#objc func handleThemesTapped() {
let themesVc = ThemesVC(collectionViewLayout: UICollectionViewFlowLayout())
let navController = UINavigationController(rootViewController: themesVc)
themesVc.delegate = self
navigationController?.present(navController, animated: true, completion: nil)
}
}
MainVC Cell with ColletionView
class QuoteCell: UICollectionViewCell, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var imageToPass = UIImage(named: "2")!
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.register(QuoteSubCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setUpViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! QuoteSubCell
cell.quote = quotes[indexPath.item]
cell.setupCell(with: imageToPass)
return cell
}
func setUpViews() {
addSubview(collectionView)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.anchors(top: nameLabel.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor)
}
}
CustomCell
class QuoteSubCell: UICollectionViewCell {
var backgroundImage: UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
view.contentMode = .scaleAspectFill
view.image = UIImage(named: "2")
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
setUpViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupCell(with image: UIImage) {
backgroundImage.image = image
}
func setUpViews() {
contentView.addSubview(backgroundImage)
backgroundImage.anchors(top: contentView.topAnchor, left: contentView.leftAnchor, bottom: contentView.bottomAnchor, right: contentView.rightAnchor)
}
}
ThemesVC where the user chooses the background
class ThemesVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var delegate: ThemeDelegate?
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
setUpViews()
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ThemeCell
cell.backgroundImage.image = themeCell.backgroundImages[indexPath.item]
cell.layer.cornerRadius = 10
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.handlePassThemeData(data: themeCell.backgroundImages[indexPath.item])
self.navigationController?.dismiss(animated: true, completion: nil)
}
func setUpViews() {
collectionView.register(ThemeCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView.backgroundColor = UIColor(white: whitePoint, alpha: 1)
collectionView.alwaysBounceVertical = true
collectionView.showsVerticalScrollIndicator = false
}
}
You almost got it right.
class MainVC: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, ThemeDelegate {
//MARK: Properties
var currentTheme = UIImage(named: "defaultImage")
override func viewDidLoad() {
super.viewDidLoad()
setUpViews()
}
func setUpViews() {
view.addSubview(collectionView)
collectionView.anchors(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor)
}
func handlePassThemeData(data: UIImage) {
self.currentTheme = data //override your variable with the new image
self.collectionView.reloadData() //reload the collectionView, to apply the change
}
#objc func handleThemesTapped() {
let themesVc = ThemesVC(collectionViewLayout: UICollectionViewFlowLayout())
themesVc.delegate = self
//no need to instantiate a new navigationController. Just push to the current one, if you want the animation from right, and not from bottom-up.
navigationController?.pushViewController(themesVc, animated: true)
}
}
Delegates should always be weak referenced.
class ThemesVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
weak var delegate: ThemeDelegate?
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.handlePassThemeData(data: themeCell.backgroundImages[indexPath.item])
self.navigationController?.popViewController(animated: true)
}
}
Also, you don't need to create a static variable inside the cell. Instead, pass your currentImage to your cell's variable imageToPass in the method cellForItemAt.
class QuoteCell: UICollectionViewCell, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var imageToPass = UIImage(named: "defaultImage")//pass this image to the cells inside the collectionView
override init(frame: CGRect) {
super.init(frame: frame)
setUpViews()
}
func setUpViews() {
addSubview(collectionView)
collectionView.anchors(top: nameLabel.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor)
}
}
Finally, your subCells should show your image:
class QuoteSubCell: UICollectionViewCell {
var backgroundImage: UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
view.contentMode = .scaleAspectFill
view.image = QuoteSubCell.chosenTheme
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
setUpViews()
}
private func setUpViews() {
contentView.addSubview(backgroundImage)
backgroundImage.anchors(top: contentView.topAnchor, left: contentView.leftAnchor, bottom: contentView.bottomAnchor, right: contentView.rightAnchor)
}
func setupCell(with image: UIImage) { //use this setup function inside "cellForItemAt" method, where you will pass the `imageToPass` image.
backgroundImage.image = image
}
}
UPDATE:
cellForItemAt inside MainVC:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! QuoteCell
createGradientLayer(bounds: CGRect(x: 0, y: 0, width: cell.frame.width, height: cell.frame.height), cell: cell)
cell.delegate = self
cell.imageToPass = currentTheme //here you pass the image the 1st time
return cell
} else if indexPath.item == 1 {
...
}
}
cellForItemAt inside QuoteCell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! QuoteSubCell
cell.quote = quotes[indexPath.item]
cell.setupCell(with: imageToPass) //here you pass the image the second time
return cell
}
If you're still unable to make it work, I will ask you to share the code from the cellForItemAt methods from the both collectionViews.
Related
When using storyboards or nibs, outlets for CollectionViews are implicitly unwrapped, so accessing and configuring the collectionView is a straightforward process.
#IBOutlet var collectionView: UICollectionView!
When creating them programmatically we can do something like this:
var collectionView: UICollectionView!
Would this be the correct approach? Theres no guarantee that the collectionView will be initialised, so there's always the possibility for a crash. I would also like to avoid having an optional since I will be forced to unwrap it every time.
I always create them as computed, as there are some attributes you want to add to it. And often lazy as well so you can set dataSource and delegate. You also wants to add the collectionViewLayout in it's initializer. Hence i'm creating the UICollectionViewFlowLayout first.
private lazy var selectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 64, height: 24)
layout.itemSize = CGSize(width: 64, height: 24)
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 12
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = .white
collectionView.showsHorizontalScrollIndicator = false
collectionView.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
return collectionView
}()
Try this example
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let viewController = MyCollectionViewController()
viewController.modalPresentationStyle = .fullScreen
present(viewController, animated: true) {}
}
}
MyCollectionViewController.swift
import UIKit
class MyCollectionViewController: UIViewController {
var dataModels: [DataModel?] = [DataModel]()
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = .white
collectionView.showsHorizontalScrollIndicator = false
collectionView.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
return collectionView
}()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init() {
super.init(nibName: nil, bundle: nil)
self.view.backgroundColor = .yellow
let data = (0...10).map { value -> DataModel in
DataModel(name: "\(value)")
}
dataModels.append(contentsOf: data)
addCollectionView()
}
func addCollectionView() {
self.view.addSubview(self.collectionView)
self.collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
self.collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
self.collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
self.collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
self.collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
extension MyCollectionViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? CollectionViewCell {
if let dataModel = dataModels[indexPath.row] {
print(">>>>\(dataModel.name)")
cell.nameLabel.text = dataModel.name
}
return cell
}
return UICollectionViewCell()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.dataModels.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
return CGSize(width: 200, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets{
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
extension MyCollectionViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
class CollectionViewCell: UICollectionViewCell {
var storeBack = UIView()
var nameLabel = UILabel()
public override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
fileprivate func commonInit() {
self.contentView.addSubview(self.storeBack)
self.storeBack.backgroundColor=UIColor.green
self.storeBack.translatesAutoresizingMaskIntoConstraints = false
self.storeBack.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true
self.storeBack.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor).isActive = true
self.storeBack.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor).isActive = true
self.storeBack.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true
self.storeBack.addSubview(self.nameLabel)
self.nameLabel.textColor = .black
self.nameLabel.textAlignment = .center
self.nameLabel.numberOfLines = 0
nameLabel.font = UIFont.systemFont(ofSize: 20)
self.nameLabel.translatesAutoresizingMaskIntoConstraints = false
self.nameLabel.topAnchor.constraint(equalTo: storeBack.topAnchor).isActive = true
self.nameLabel.leadingAnchor.constraint(equalTo: storeBack.leadingAnchor).isActive = true
self.nameLabel.trailingAnchor.constraint(equalTo: storeBack.trailingAnchor).isActive = true
self.nameLabel.bottomAnchor.constraint(equalTo: storeBack.bottomAnchor).isActive = true
}
}
struct DataModel: Codable {
var name: String
}
I want to implement UITableView's UISwipeActionsConfiguration for a UICollectionView. In order to do so, I am using SwipeCellKit - github
My UICollectionView adopts to the SwipeCollectionViewCellDelegate protocol. And the cell inherits from SwipeCollectionViewCell.
ViewController with UICollectionView
class SwipeViewController: UIViewController {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(SwipeableCollectionViewCell.self, forCellWithReuseIdentifier: SwipeableCollectionViewCell.identifier)
collectionView.showsVerticalScrollIndicator = false
collectionView.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 4, right: 0)
collectionView.backgroundColor = UIColor(white: 0.97, alpha: 1)
collectionView.dataSource = self
collectionView.delegate = self
return collectionView
}()
var items: [String] = {
var items = [String]()
for i in 1 ..< 20 {
items.append("Item \(i)")
}
return items
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.setConstraints(topAnchor: view.topAnchor,
leadingAnchor: view.leadingAnchor,
bottomAnchor: view.bottomAnchor,
trailingAnchor: view.trailingAnchor,
leadingConstant: 10,
trailingConstant: 10)
}
}
extension SwipeViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 80)
}
}
extension SwipeViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SwipeableCollectionViewCell.identifier, for: indexPath) as! SwipeableCollectionViewCell
cell.backgroundColor = BackgroundColor.colors[indexPath.row]
cell.delegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
extension SwipeViewController: SwipeCollectionViewCellDelegate {
func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
}
return [deleteAction]
}
}
SwipeCollectionViewCell
class SwipeableCollectionViewCell: SwipeCollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(nameLabel)
nameLabel.setConstraints(topAnchor: self.topAnchor,
leadingAnchor: self.leadingAnchor,
bottomAnchor: self.bottomAnchor,
trailingAnchor: self.trailingAnchor)
self.backgroundColor = .white
}
static let identifier = "TaskListTableViewCell"
private let nameLabel: UILabel = {
let label = UILabel()
label.text = "Simulator user has requested new graphics quality"
label.numberOfLines = 0
return label
}()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
After doing so, when I swipe the cell, the deleteAction overlaps with the content of the cell.
As you see in the screenshot, the cell's content overlaps with the deleteAction text.
Update
setConstraints sets views's translatesAutoresizingMaskIntoConstraints to false
You must add nameLabel to contentView.
Change
self.addSubview(nameLabel)
nameLabel.setConstraints(topAnchor: self.topAnchor,
leadingAnchor: self.leadingAnchor,
bottomAnchor: self.bottomAnchor,
trailingAnchor: self.trailingAnchor)
to
self.contentView.addSubview(nameLabel)
nameLabel.setConstraints(topAnchor: self.contentView.topAnchor,
leadingAnchor: self.contentView.leadingAnchor,
bottomAnchor: self.contentView.bottomAnchor,
trailingAnchor: self.contentView.trailingAnchor)
Result:
I've gotten my cells to overlap by setting the minimumLineSpacing property of the collection views layout to negative. But, when I scroll and the cells are redrawn, they now overlap in the opposite direction. I've put pictures below.
How do I keep the cells overlapping as seen in the first picture when the collection view is scrolled and cells are redraw?
import UIKit
class PopularView: UIView {
let cellID = "cellID"
// MARK: - Views
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.minimumLineSpacing = -55 // -------Allows Overlap-----
layout.itemSize = CGSize(width: SCREEN_WIDTH, height: 185)
layout.minimumInteritemSpacing = 17
let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
view.backgroundColor = .white
return view
}()
// MARK: - Initializers
override init(frame: CGRect) {
super.init(frame: frame)
collectionView.dataSource = self
collectionView.register(PopularCell.self, forCellWithReuseIdentifier: cellID)
backgroundColor = .white
setupCollectionView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Setup
fileprivate func setupCollectionView() {
self.addSubview(collectionView)
collectionView.anchors(top: self.topAnchor, topPad: 0, bottom: self.bottomAnchor, bottomPad: 0, left: self.leftAnchor, leftPad: 0, right: self.rightAnchor, rightPad: 0, height: nil, width: nil)
collectionView.contentSize = CGSize(width: 700, height: 700)
}
}
extension PopularView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 500
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! PopularCell
cell.background.backgroundColor = .random
return cell
}
}
Try this, that might help you:
1- Inside your Cells, you could define innerView inside your cell and set the frame to
let innerView:UIView = CGRect(x: 0,y: -overlapHeight,width: screenWidth, height:cell.height + overlapHeight)
cell?.contentView.addSubview(innerView)
2- Configure your cell during initialisation with this:
cell?.contentView.clipsToBounds = false
3- When loading cell, set the z-order:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.layer.zPosition = CGFloat(indexPath.row)
// configure your cell after here
}
You should be able to see the nested views inside your content view to have overlapping.
I have drafted a sample code, not looking perfectly, but will help you get started:
private let reuseIdentifier = "Cell"
private let overlapHeight:CGFloat = 100
class CustomCollectionCell:UICollectionViewCell {
var innerView:UIView?
override init(frame: CGRect) {
super.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 300))
self.backgroundColor = .darkGray
let innerView = UIView(frame: CGRect(x: 0,y: -overlapHeight,width: UIScreen.main.bounds.width,height: overlapHeight + self.contentView.frame.height))
self.innerView = innerView
innerView.layer.cornerRadius = 20
self.contentView.addSubview(innerView)
self.contentView.clipsToBounds = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(color:UIColor?) {
innerView?.backgroundColor = color
}
}
import UIKit
private let reuseIdentifier = "Cell"
private let overlapHeight:CGFloat = 100
class CustomCollectionCell:UICollectionViewCell {
var innerView:UIView?
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .darkGray
let innerView = UIView(frame: CGRect(x: 0,y: -overlapHeight,width: UIScreen.main.bounds.width,height: overlapHeight + self.contentView.frame.height))
self.innerView = innerView
innerView.layer.cornerRadius = 20
self.contentView.addSubview(innerView)
self.contentView.clipsToBounds = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(color:UIColor?) {
innerView?.backgroundColor = color
}
}
class CollectionViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width, height: 190)
flowLayout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
flowLayout.scrollDirection = .vertical
flowLayout.minimumInteritemSpacing = 0.0
collectionView.collectionViewLayout = flowLayout
// Register cell classes
self.collectionView!.register(CustomCollectionCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 30
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.layer.zPosition = CGFloat(indexPath.row)
var color:UIColor?
switch indexPath.row % 4 {
case 0:
color = .purple
case 1:
color = .yellow
case 2:
color = .green
default:
color = .red
}
if let cell = cell as? CustomCollectionCell {
cell.configure(color: color)
}
return cell
}
}
Result:
import UIKit
private let reuseIdentifier = "NewCell"
class SeondViewController: UIViewController, UICollectionViewDelegateFlowLayout {
#IBOutlet var myNavBar: UINavigationBar!
var frutta: [String]!
override func viewDidLoad() {
super.viewDidLoad()
let margins = self.view.layoutMarginsGuide
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 30, left: 10, bottom: 10, right: 10)
layout.scrollDirection = .vertical
let newCollectionView: UICollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
newCollectionView.dataSource = self
newCollectionView.delegate = self
newCollectionView.register(SecondTabCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
newCollectionView.reloadData()
newCollectionView.backgroundColor = UIColor.clear
newCollectionView.isHidden = false
newCollectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(newCollectionView)
frutta = ["Mele", "Olive", "Pere", "Noci", "Banane", "Kiwi", "Ananas"]
myNavBar.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
myNavBar.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
myNavBar.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
newCollectionView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
newCollectionView.topAnchor.constraint(equalTo: myNavBar.bottomAnchor).isActive = true
newCollectionView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
◦ newCollectionView.bottomAnchor.constraint(equalTo: margins.bottomAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.size.width / 2, height: collectionView.bounds.size.height / 3)
}
}
extension SecondViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return frutta.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SecondTabCollectionViewCell
myCell.backgroundColor = UIColor.clear
myCell.secondCellLabel = UILabel()
myCell.secondCellLabel.frame.size = CGSize(width: myCell.bounds.width / 2, height: myCell.secondCellLabel.bounds.height / 2)
myCell.secondCellLabel.adjustsFontSizeToFitWidth = true
myCell.secondCellLabel.backgroundColor = UIColor.clear
myCell.secondCellLabel.textColor = UIColor.black
myCell.secondCellLabel.text = frutta[indexPath.item]
myCell.translatesAutoresizingMaskIntoConstraints = false
myCell.contentView.addSubview(myCell.secondCellLabel)
return myCell
}
}
extension SecondViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Hai premuto \(indexPath.row).")
}
}
import UIKit
class SecondTabCollectionViewCell: UICollectionViewCell {
var secondCellLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) non e stato implementato.")
}
}
myCell.secondCellLabel.frame.size = CGSize(width: myCell.bounds.width / 2, height: myCell.secondCellLabel.bounds.height / 2)
The above line is wrong in cellForItemAt, you put the height of label is myCell.secondCellLabel.bounds.height instead of myCell.bounds.height.
correct one is
myCell.secondCellLabel.frame.size = CGSize(width: myCell.bounds.width / 2, height: myCell.bounds.height / 2)
It is a little strange to be laying out a cell's internal views in cellForRowAtIndexPath, this should be done in the cell class itself. The basic problem is that you your UILabel is appearing in the top left corner of the cell and has a height of zero, so you cannot see it. I moved the code you had in cellForRowAtIndexPath into the cell itself.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SecondTabCollectionViewCell
myCell.backgroundColor = UIColor.clear
myCell.secondCellLabel.text = frutta[indexPath.item]
return myCell
}
import UIKit
class SecondTabCollectionViewCell: UICollectionViewCell {
var secondCellLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .clear
label.textColor = .black
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func configureViews() {
addSubview(secondCellLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
configureLayout()
}
private func configureLayout() {
let width = bounds.width / 2
let height = frame.height / 2
let size = CGSize(width: width, height: height)
let origin = CGPoint(x: width/2, y: height/2)
secondCellLabel.frame = CGRect(origin: origin, size: size)
}
}
I am trying to create an app which uses a collectionView as its base for paging between screens. For one of the features I would like to insert a screen(collectionView Cell) when the user taps on a button and remove the screen(collectionView Cell) when the user taps the button again. I have tried implementing insert and delete methods shown below however when they are called it seems to just slide the cells down without letting me modify them.When the cells are slid down it creates a glitchy user experience. How can I add a cell modify it and then remove it without interfering with the user experience?
Below is the code where the cells are added and removed:
func addCell(sender:Any?){
self.numberOfItemsInSections += 1
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: IndexPath(item:1,section:0)) as! customCell
cell.backgroundColor = .purple
//newCell.label.text = "new cell added"
self.collectionView.insertItems(at: [IndexPath(item:1,section:0)])
}
func removeCell(sender: Any?) {
self.numberOfItemsInSections -= 1
self.collectionView.deleteItems(at: [IndexPath(item:1,section:0)])
}
Below is the code in its entirety
import UIKit
class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout,addRemoveCell {
#IBOutlet weak var collectionView: UICollectionView!
var numberOfItemsInSections:Int = 3
var cellType:[String] = ["customCell"]
var cellClass:[Any]!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(customCell.self, forCellWithReuseIdentifier: "customCell")
collectionView.isScrollEnabled = true
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView.collectionViewLayout = layout
collectionView.isPagingEnabled = true
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numberOfItemsInSections
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.view.frame.width, height: self.view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as! customCell
cell.addRemoveCellDelegate = self
cell.label.text = "\(indexPath)"
cell.backgroundColor = .blue
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func addCell(sender:Any?){
self.numberOfItemsInSections += 1
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: IndexPath(item:1,section:0)) as! customCell
cell.backgroundColor = .purple
//newCell.label.text = "new cell added"
self.collectionView.insertItems(at: [IndexPath(item:1,section:0)])
}
func removeCell(sender: Any?) {
self.numberOfItemsInSections -= 1
self.collectionView.deleteItems(at: [IndexPath(item:1,section:0)])
}
}
protocol addRemoveCell{
func addCell(sender:Any?)
func removeCell(sender:Any?)
}
class customCell:UICollectionViewCell{
var button:UIButton!
var label:UILabel!
var addRemoveCellDelegate:addRemoveCell?
var addCell:Bool = true
override init(frame: CGRect) {
super.init(frame: frame)
self.button = UIButton(frame: CGRect(x: self.frame.midX, y: self.frame.midX, width: self.frame.midX, height: self.frame.midX))
self.addSubview(button)
label = UILabel(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height/8))
self.addSubview(label)
label.textAlignment = .center
label.adjustsFontSizeToFitWidth = true
label.backgroundColor = .green
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button.backgroundColor = .orange
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0).isActive = true
button.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0).isActive = true
button.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -18).isActive = true
button.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 18).isActive = true
button.heightAnchor.constraint(equalToConstant: 70).isActive = true
}
#objc func buttonTapped(){
print("The button was tapped")
if addCell{
if let delegate = addRemoveCellDelegate{
print("Inside of addCell")
delegate.addCell(sender: self)
}
addCell = !addCell
}
else{
if let delegate = addRemoveCellDelegate{
delegate.removeCell(sender: self)
}
addCell = !addCell
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}