View is calling viewDidLoad() when navigating back - ios

When the user selects a cell in the collection view, it pushes to a new view controller for that cell.
The problem is that when the user swipes back, the collection view controller runs viewDidLoad() instead of viewDidAppear(). This causes the whole collection view to reload and go back up to the top (first cell) and the user has to scroll all the way back down to get to where they were before.
Does anyone know why this is happening??
import UIKit
import FirebaseStorage
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let reuseIdentifier = "PostCell"
var post: [Post] = [Post]()
var imageURLs: [URL] = [URL]()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.hidesBarsOnSwipe = true
collectionView?.backgroundColor = UIColor(red: 0.91, green: 0.91, blue: 0.91, alpha: 1.00)
// Uncomment the following line to preserve selection between presentations
self.clearsSelectionOnViewWillAppear = false
// Register cell classes
collectionView?.register(PostCell.self, forCellWithReuseIdentifier: reuseIdentifier)
//setupHorizontalBar()
setupCollectionView()
// Get all of the posts
loadPosts()
print("loaded")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
print("appeared")
collectionView.index
}
func setupCollectionView() {
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .vertical
layout.minimumLineSpacing = 0
}
}
func loadPosts() {
postRef.observe(.value, with: { (snapshot) in
for eachPost in snapshot.value as! [String: Any] {
let dict: Dictionary<String, Any> = [eachPost.key: eachPost.value]
let post = Post(postDictionary: dict)
print(post)
self.posts.append(post)
self.collectionView?.reloadData()
}
})
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PostCell
//cell.backgroundColor = .brown
let post = posts[indexPath.row]
cell.nameLabel.text = post.name
let details = "\n\(post.address!)\n\n\(post.time!) \(post.date!)"
cell.postDetailTextView.text = details
if let imageURL = post.image {
print(imageURL)
cell.postImageView.sd_setImage(with: URL(string: imageURL))
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = (self.view.frame.width - 20) * 9 / 16
return CGSize(width: self.view.frame.width, height: height + 5 + 140)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
When didSelectItemAt is called, I want the navigation controller to push to another view controller for the cell selected.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath)
let detailVC = DetailViewController()
self.navigationController?.pushViewController(detailVC, animated: true)
}
}

Related

Collection view auto resizing and setup repeating background

I am using collection view to load data from API. Here i want to extend size of collection view height instead of scrolling inside collection view. And also need to repeat the background image according to collection view height.
Here is the android layout and i want to develop similar to this.Tap here
import UIKit
import Nuke
import SVProgressHUD
import JSONJoy
class HomeViewController: UIViewController {
#IBOutlet weak var categoryCollection: UICollectionView!
#IBOutlet weak var tabbar: UITabBar!
var sectors:[Sector] = []
var timer = Timer()
var counter = 0
var selectedSector = ""
var selectedSectorName = ""
var webService = ApiService()
let plist = PlistHelper()
override func viewDidLoad() {
super.viewDidLoad()
self.categoryCollection.dataSource = self
self.categoryCollection.delegate = self
for item in tabbar.items ?? []{
item.image = item.image?.withRenderingMode(.alwaysOriginal)
}
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: .selected)
listSectors()
self.categoryCollection.backgroundColor = UIColor(patternImage: UIImage(named: "bg")!)
}
override func viewWillAppear(_ animated: Bool) {
listbanners()
}
override func viewWillDisappear(_ animated: Bool) {
self.timer.invalidate()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "sectors"){
let vc = segue.destination as! SectorsViewController
vc.sectorCode = selectedSector
vc.sectorName = selectedSectorName
}
}
func listSectors(){
webService.listSectors({ (sectors, message, status) in
if(status){
if let resData = sectors.arrayObject {
do{
for data in resData{
self.sectors.append(try Sector(JSONLoader(data)))
}
DispatchQueue.main.async {
self.categoryCollection.reloadData()
}
}
catch let error {
print("JSonJoyError:\(error)")
}
}
}
})
}
}
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(collectionView == bannerCollection){
return banners.count
}
else {
return sectors.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let options = ImageLoadingOptions(placeholder: UIImage(named: "bannerPlaceholder"),transition: .fadeIn(duration: 0.33))
if(collectionView == bannerCollection){
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! DataCollectionViewCell
Nuke.loadImage(with: URL(string: banners[indexPath.row].ImageUrl ?? "")!, options: options, into:cell.img)
return cell
}
else{
let cell = categoryCollection.dequeueReusableCell(withReuseIdentifier: "catCell", for: indexPath) as! catogeryCollectionViewCell
Nuke.loadImage(with: URL(string: sectors[indexPath.row].ImageUrl ?? "")!, options: options, into:cell.photoImageView)
return cell
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if(collectionView == categoryCollection){
selectedSector = sectors[indexPath.row].Code ?? "FOOD"
selectedSectorName = sectors[indexPath.row].Name ?? "FOOD"
self.performSegue(withIdentifier: "sectors", sender: self)
}
}
}
extension HomeViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if(collectionView == bannerCollection){
let size = bannerCollection.frame.size
return CGSize(width: size.width, height: size.height - 10)
}
else{
let size = categoryCollection.frame.size
print("size\(size)")
return CGSize(width: (size.width / 2) - 8, height:120)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 30
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0.0
}
}
Following steps can help you to increase your collection View height according to data.
Create Heightconstraint outlet.
After loading data in collection view with delay of 0.2 sec in main thread,
Set Height Constraint constant = collection view content size height.

As soon as I add the constraints in the inner CollectionView in my Nested CollectionView, it's starts a never ending loop

Controller One is the first Collection view controller, From here, at cell selection, it navigates me to the controller two.
In Controller two I have two Nested Collection View.
A horizontal Collection View inside Vertical Collection View Cell.
My Issue is that when I try to add Any UI elements in Horizontal Collection View Cell then I could not be able to navigate from the first controller itself.
After spending a lot of time debugging the code, I found that when I add any UI element constraint on horizontal Collection View cell then this thing happens.But I don't know the solution.
Below is the code of both the Controller and sorry if I am not clear about the problem.
ControllerOne
import Foundation
import UIKit
import FirebaseDatabase
import CodableFirebase
class HomeViewController : BaseViewController{
lazy var categoryCollectionView = UICollectionView()
var navModel: [NavigationModel] = []
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .init(displayP3Red: 72, green: 54, blue: 13, alpha: 0.1)
self.navigationController?.navigationBar.barTintColor = .black
self.navigationController?.navigationBar.tintColor = .yellow
self.navigationController?.navigationItem.title = "Title"
}
override func SetupUI() {
//Sessions/Menu Grid/List
let verticalFlowLayout = UICollectionViewFlowLayout()
verticalFlowLayout.scrollDirection = .vertical
self.categoryCollectionView = UICollectionView(frame: .zero , collectionViewLayout: verticalFlowLayout)
self.categoryCollectionView.delegate = self
self.categoryCollectionView.dataSource = self
self.categoryCollectionView.backgroundColor = .clear
self.categoryCollectionView.register(HomeCategoryCell.self,forCellWithReuseIdentifier: AppConstants.CellIdentifier.CategoryCellId)
self.categoryCollectionView.showsVerticalScrollIndicator = false
}
override func SetViewConstraints() {
self.view.addSubview(categoryCollectionView)
categoryCollectionView.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview().inset(30)
make.height.equalToSuperview()
make.centerX.equalTo(self.view)
make.top.equalTo(self.view.safeAreaLayoutGuide)
}
}
}
extension HomeViewController : UICollectionViewDelegate,
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return navModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : HomeCategoryCell = categoryCollectionView.dequeueReusableCell(withReuseIdentifier: AppConstants.CellIdentifier.CategoryCellId, for: indexPath) as! HomeCategoryCell
cell.categoryLabel.text = navModel[indexPath.row].label
cell.imageURL = navModel[indexPath.row].backgroundImageURL
cell.contentView.sizeToFit()
cell.contentView.layoutIfNeeded()
cell.contentView.autoresizingMask = [UIView.AutoresizingMask.flexibleHeight]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (view.frame.size.width - 60)
return CGSize(width: width, height: 120)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let controller = SelectedCategoryViewController()
controller.pageModel = navModel[indexPath.row].pages!
self.navigationController?.pushViewController(controller, animated: true)
}
}
Controller Two
import Foundation
import UIKit
class SelectedCategoryViewController: BaseViewController {
lazy var subCategoryCollectionView = UICollectionView()
var pageModel: [PagesModel] = []
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .init(displayP3Red: 72, green: 54, blue: 13, alpha: 0.1)
}
override func SetupUI() {
//Sessions/Menu Grid/List
let verticalFlowLayout = UICollectionViewFlowLayout()
verticalFlowLayout.scrollDirection = .vertical
self.subCategoryCollectionView = UICollectionView(frame: .zero , collectionViewLayout: verticalFlowLayout)
self.subCategoryCollectionView.delegate = self
self.subCategoryCollectionView.dataSource = self
self.subCategoryCollectionView.backgroundColor = .init(displayP3Red: 72, green: 54, blue: 13, alpha: 0.1)
self.subCategoryCollectionView.register(SelectedCategoryCell.self,forCellWithReuseIdentifier: AppConstants.CellIdentifier.SelectedCategoryCellId)
self.subCategoryCollectionView.showsVerticalScrollIndicator = false
}
override func SetViewConstraints() {
self.view.addSubview(subCategoryCollectionView)
subCategoryCollectionView.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview()
make.height.equalToSuperview()
make.centerX.equalToSuperview()
make.top.equalTo(self.view.safeAreaLayoutGuide)
}
}
}
extension SelectedCategoryViewController : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pageModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : SelectedCategoryCell = subCategoryCollectionView.dequeueReusableCell(withReuseIdentifier: AppConstants.CellIdentifier.SelectedCategoryCellId, for: indexPath) as! SelectedCategoryCell
cell.subCategoryLabel.text = pageModel[indexPath.row].label
cell.contentView.sizeToFit()
cell.contentView.layoutIfNeeded()
cell.contentView.autoresizingMask = [UIView.AutoresizingMask.flexibleHeight]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (view.frame.size.width)
return CGSize(width: width, height: 220)
}
}
SelectedCategoryCell
import Foundation
import UIKit
class SelectedCategoryCell: BaseUICollectionViewCell {
var subCategoryLabel = UILabel()
lazy var subCategorySessionCollectionView = UICollectionView()
override func layoutSubviews() {
super.layoutSubviews()
}
override func SetupUI() {
subCategoryLabel.textColor = .white
subCategoryLabel.textAlignment = .left
let horizontalFlowLayout = UICollectionViewFlowLayout()
horizontalFlowLayout.scrollDirection = .horizontal
self.subCategorySessionCollectionView = UICollectionView(frame: .zero , collectionViewLayout: horizontalFlowLayout)
self.subCategorySessionCollectionView.delegate = self
self.subCategorySessionCollectionView.dataSource = self
self.subCategorySessionCollectionView.backgroundColor = .init(displayP3Red: 72, green: 54, blue: 13, alpha: 0.1)
self.subCategorySessionCollectionView.register(SubCategorySessionCell.self,forCellWithReuseIdentifier: AppConstants.CellIdentifier.SubCategorySessionCellId)
self.subCategorySessionCollectionView.showsHorizontalScrollIndicator = false
}
override func SetViewConstraints() {
self.contentView.addSubview(subCategoryLabel)
subCategoryLabel.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview()
make.height.equalTo(40)
make.leftMargin.equalTo(10)
make.top.equalTo(self.contentView.snp.top)
}
self.contentView.addSubview(subCategorySessionCollectionView)
subCategorySessionCollectionView.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview()
make.height.equalTo(160)
make.leftMargin.equalTo(10)
make.top.equalTo(self.subCategoryLabel.snp.bottom)
}
}
}
extension SelectedCategoryCell : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : SubCategorySessionCell = subCategorySessionCollectionView.dequeueReusableCell(withReuseIdentifier: AppConstants.CellIdentifier.SubCategorySessionCellId, for: indexPath) as! SubCategorySessionCell
cell.backgroundColor = .white
cell.contentView.sizeToFit()
cell.contentView.layoutIfNeeded()
cell.contentView.autoresizingMask = [UIView.AutoresizingMask.flexibleHeight]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// let width = (contentView.frame.size.width)
return CGSize(width: 160, height: 160)
}
}
Issue Here in this cell Class
import Foundation
import UIKit
import Kingfisher
import SDWebImage
class SubCategorySessionCell: BaseUICollectionViewCell {
var backgroundImageView = UILabel()
var imageURL : String = ""
override func layoutSubviews() {
super.layoutSubviews()
}
override func SetupUI() {
self.backgroundImageView.backgroundColor = .black
}
override func SetViewConstraints() {
self.contentView.addSubview(backgroundImageView)
backgroundImageView.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview()
make.height.equalTo(40)
make.leftMargin.equalTo(10)
make.top.equalTo(self.contentView.snp.top)
}
}
}

want to add a collectionView inside tableView by downloading data from firebase to show . ios App Swift

want to add a UICollectionView inside UITableView by downloading data from firebase to show
Now I'm done with the interface part, but I'm stuck in the problem, can't bring data from firebase to show in the UICollectionView.
I can't run collectionView.reloadData() because the UICollectionView is different in class, how should I fix it?
func showImageRewardData(rewardID:String) {
let databaseRef = Database.database().reference().child("reward").child(rewardID).child("rewardImage")
databaseRef.observe(DataEventType.value, with: { (Snapshot) in
if Snapshot.childrenCount>0{
self.rewardDataArr.removeAll()
for rewardImage in Snapshot.children.allObjects as! [DataSnapshot]{
let rewardObject = rewardImage.value as? [String: AnyObject]
if(rewardObject?["imageURL"] != nil){
let imageUrl = rewardObject?["imageURL"]
let Data = rewardDetailClass(rewardImage: imageUrl as? String)
self.rewardDataArr.insert(Data, at: 0)
}
YOURVIEWCONTROLLER.SWIFT
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellIdentifier: String = "Cell_NotesList"
var cell: Cell_NotesList? = (tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as? Cell_NotesList)
if cell == nil {
let topLevelObjects: [Any] = Bundle.main.loadNibNamed("Cell_NotesList", owner: nil, options: nil)!
cell = (topLevelObjects[0] as? Cell_NotesList)
cell?.selectionStyle = .none
}
cell?.reloadCollectionView(arr: arrofofthumbImages)
return cell!
}
YourCell.SWIFT
class Cell_NotesList: UITableViewCell {
var imagesArr = NSMutableArray()
override func awakeFromNib() {
collectionView.register(UINib.init(nibName: "cell_ImageCollection", bundle: nil), forCellWithReuseIdentifier: "cell_ImageCollection")
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension Cell_NotesList : UICollectionViewDataSource {
func reloadCollectionView(arr:NSMutableArray) {
imagesArr = arr
collectionView.reloadData()
self.layoutIfNeeded()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagesArr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : cell_ImageCollection? = collectionView.dequeueReusableCell(withReuseIdentifier: "cell_ImageCollection", for: indexPath) as? cell_ImageCollection
//YOUR CODE HERE
return cell!
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
extension Cell_NotesList : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 120.0, height: 120.0)
}
}
extension UICollectionViewCell {
var indexPath: IndexPath? {
return (superview as? UICollectionView)?.indexPath(for: self)
}
}
In class that extends UICollectionView, put one setter method like
func setData(data: String){
// Update your cell view here like
Label.text = data
}

UICollectionView reloadData() does not update cells in the collection view

Here's high level description of what I'm trying to achieve;
1. fetch data
2. save the fetched data in an array object
3. update collection view with the size of the array
Here's my code
class ItemcollectionViewController:UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "CellId"
var categories = [Category]()
let viewOptionVar:ViewOptionBar = {
let vOV = ViewOptionBar()
vOV.translatesAutoresizingMaskIntoConstraints = false
return vOV
}()
private func fetchData() {
let categoryController = CategoryController()
categoryController.getAllCategory(username: "blah", password: "password") {(returnedCategories, error) -> Void in
if error != nil {
print(error)
return
}
self.categories = returnedCategories!
print("size of the array is \(self.categories.count)")
OperationQueue.main.addOperation{self.collectionView?.reloadData()}
}
}
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
collectionView?.backgroundColor = UIColor.white
collectionView?.register(ItemCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.contentInset = UIEdgeInsetsMake(50, 0, self.view.frame.height, self.view.frame.width)
collectionView?.scrollIndicatorInsets = UIEdgeInsetsMake(50, 0, 0, self.view.frame.width)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("in the method \(self.categories.count)")
return self.categories.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ItemCell
cell.category = categories[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 111, height: 111)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
private func setupViweOptionBar() {
view.addSubview(viewOptionVar)
view.addConstraintsWithFormat(format: "H:|[v0]|", views: viewOptionVar)
view.addConstraintsWithFormat(format: "V:|[v0(50)]", views: viewOptionVar)
}
}
In the log I could see the following statements:
in the method 0
size of the array is 3
and could not see any cell from my view.
Can someone advise me what I've done wrong?
Thanks in advance.
EDIT 1
Now I'm fetching data after registering the customised cells. However, it still doesn't work
updated code:
class ItemcollectionViewController:UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "CellId"
var categories = [Category]()
let viewOptionVar:ViewOptionBar = {
let vOV = ViewOptionBar()
vOV.translatesAutoresizingMaskIntoConstraints = false
return vOV
}()
private func fetchData() {
let categoryController = CategoryController()
categoryController.getAllCategory(username: "blah", password: "password") {(returnedCategories, error) -> Void in
if error != nil {
print(error)
return
}
self.categories = returnedCategories!
print("size of the array is \(self.categories.count)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.white
collectionView?.register(ItemCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.contentInset = UIEdgeInsetsMake(50, 0, self.view.frame.height, self.view.frame.width)
collectionView?.scrollIndicatorInsets = UIEdgeInsetsMake(50, 0, 0, self.view.frame.width)
collectionView?.dataSource = self
collectionView?.delegate = self
fetchData()
DispatchQueue.main.async{self.collectionView?.reloadData()}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("in the method \(self.categories.count)")
return self.categories.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ItemCell
cell.category = categories[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 111, height: 111)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
private func setupViweOptionBar() {
view.addSubview(viewOptionVar)
view.addConstraintsWithFormat(format: "H:|[v0]|", views: viewOptionVar)
view.addConstraintsWithFormat(format: "V:|[v0(50)]", views: viewOptionVar)
}
}
EDIT 2
The following code is my querying method
func getAllCategory(username:String, password:String, callback: #escaping ([Category]?, String?) -> Void){
var categories = [Category]()
let fetchCategories = URL(string: userURL + "all")
URLSession.shared.dataTask(with: fetchCategories!, completionHandler: { (data, response, error) in
if let err = error {
print(err)
return
}
do {
let jsonCategoryObj = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [[String: AnyObject]]
for categoryDictionary in jsonCategoryObj {
let category = Category()
category.categoryId = categoryDictionary["categoryId"] as? String
category.categoryName = categoryDictionary["categoryName"] as? String
category.categoryDescription = categoryDictionary["categoryDescription"] as? String
let categoryRegisteredDateString = categoryDictionary["categoryRegisteredDate"] as? String
let df = DateFormatter()
df.dateFormat = self.shapeDateFormat
let categoryRegisteredDate = df.date(from: categoryRegisteredDateString!)!
category.categoryRegisteredDate = categoryRegisteredDate
categories.append(category)
}
callback(categories, nil)
}catch let jsonError {
callback(nil, String(describing: jsonError))
}
}).resume()
}
FYI: I know I'm not using passed user credential, it's just a copy and paste error from my different query method
When the DataSource changes, reloadData does not update the cell that has been displayed in the view. Reload visible items will do this job.
self.collectionView.reloadData()
self.collectionView.performBatchUpdates({ [weak self] in
let visibleItems = self?.collectionView.indexPathsForVisibleItems ?? []
self?.collectionView.reloadItems(at: visibleItems)
}, completion: { (_) in
})
I'm not sure how this resolved this issue. But I just added
print("size of the array is \(self.categories?.count)")
just next to
OperationQueue.main.addOperation{self.collectionView?.reloadData()}
and it magically works.. even thought when I go back and come back to the screen, it does not show anything.
I'll investigate it more and try to find out why this is happening
Updated
Using
DispatchQueue.main.sync
instead of
DispatchQueue.main.async
resolved the problem.
DispatchQueue.main.async{self.collectionView?.reloadData()}
collectionView?.backgroundColor = UIColor.white
collectionView?.register(ItemCell.self, forCellWithReuseIdentifier: cellId)
You are reloading data before you even have registered your cell.
Register your cell and datasource and delegate methods FIRST, and reload data LAST.
EDIT:
I see you edited your post.
But again, you are fetchData() before you even have registered your cell. So, again, move the fetchData method AFTER you have registered all cells , datasources and delegates.

UICollection NumberOfItemsInSections issue with adding a constant to vars

I have two different cell type classes and i'm trying to make the first indexpath with the class PayNowCell, have it remain displayed. While the other cells are of class CheckOutCell. Now the problem is in my func numberofitemsinsection. Currently i'm using return checkout.count but it missing the PayNowCell when view loads. If i make it return checkout.count+1 to always have the PayNowCell available; my program crashes giving me the error index out of bounds. The array checkout is a global var. Can someone explain why and provide a fix? Been stuck on this for a while. Code Below.
class CheckoutController: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var inventoryTabController: InventoryTabController?
override init(frame: CGRect){
super.init(frame: frame)
setupViews()
NotificationCenter.default.addObserver(forName: .arrayValueChanged, object: nil, queue: OperationQueue.main) { [weak self] (notif) in
self?.collectionView.reloadData()
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func loadItems() -> [Item]? {
return NSKeyedUnarchiver.unarchiveObject(withFile: Item.ArchiveURL.path) as? [Item]
}
func saveItems() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(checkout, toFile: Item.ArchiveURL.path)
if !isSuccessfulSave {
print("Failed to save items...")
}
}
func addItem(item: Item) {
items.append(item)
collectionView.reloadData()
}
func editItem(item: Item, index: Int) {
items[index] = item
collectionView.reloadData()
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Identify which segue is occuring.
if segue.identifier == "ShowDetail" {
let itemDetailViewController = segue.destination as! AddItemController
// Get the cell that generated this segue.
if let selectedItemCell = sender as? InventoryCell {
let indexPath = collectionView.indexPath(for: selectedItemCell)!
let selectedItem = items[indexPath.row]
itemDetailViewController.item = selectedItem
}
}
else if segue.identifier == "AddItem" {
print("Adding new meal.")
}
}
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.dataSource = self
cv.delegate = self
cv.backgroundColor = UIColor.rgb(r: 247, g: 247, b: 247)
return cv
}()
let cellId = "cellId"
let paynow = "paynow"
func setupViews() {
backgroundColor = .brown
addSubview(collectionView)
addConstraintsWithFormat("H:|[v0]|", views: collectionView)
addConstraintsWithFormat("V:|[v0]|", views: collectionView)
collectionView.indicatorStyle = UIScrollViewIndicatorStyle.white
collectionView.register(PayNowCell.self, forCellWithReuseIdentifier: paynow)
collectionView.register(CheckoutCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return checkout.count //init number of cells
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "paynow", for: indexPath) as! PayNowCell //init cells
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CheckoutCell //init cells
print("Printing this \(checkout.count)")
cell.item = checkout[indexPath.item]
return cell
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//let item = items[indexPath.item]
//inventoryController?.showItemDetailForItem(item: item, index: indexPath.item)
print("selected")
print(indexPath.item)
collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0 {
return CGSize(width:frame.width, height: 100) //each cell dimensions
}else{
return CGSize(width:frame.width, height: 150) //each cell dimensions
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
It's better to re-arrange your collection view to have 2 sections:
Section 0: PayNow cell (only 1 cell)
Section 1: Checkout cells (using checkout array list)
Then you don't have any confusion about the indexPath.item issue.
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return section == 0 ? 1 : checkout.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "paynow", for: indexPath) as! PayNowCell //init cells
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CheckoutCell //init cells
print("Printing this \(checkout.count)")
cell.item = checkout[indexPath.item]
return cell
}
}
You need to subtract 1 from your cellForRow function. Since you are adding the PayNowCell you are adding one extra indexPath, but currently, you are using the default indexPath given by the dataSource function. That index will always be one higher than the count of your items. By subtracting 1 from the indexPath, you will be back in sync with your itemsArray, taking into account the PayNowCell.
cell.item = checkout[indexPath.item - 1]

Resources