UICollection View is not reloading while Navigating back? - ios

I need to reload the collection view while navigating back to that particular view.
I tried implementing all the following things but its not working
1.calling self.collectionView.reloadData() in viewDidAppear()
2.calling self.collectionView.reloadData() in viewWillAppear()
even i tried calling viewDidLoad() in viewDidAppear but yet it is not working.
And checked the other same type of questions but it is not working...
the only way it works is by performing segue action but I dont think so it is efficent can anyone suggest me any idea??
enter code here class RoomDeviceListVC: UIViewController , UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout ,segueAction{
let functions = DataFunctionalities()
#IBOutlet weak var collectionView1: UICollectionView!
#IBOutlet weak var collectionView2: UICollectionView!
var horizontalBarLeadingConstraint : NSLayoutConstraint?
static var isVisible : Bool?
static var CollectionViewSection : Int!
static var reloadFlag : Int!
static var delegates : CollectionViewRotation? = nil
var uiFunctions = UIFunctions()
var selectedIndex = IndexPath(item: 0, section: 0)
var constant : CGFloat?
let headerView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
uiFunctions.setViewBackground(view: self.view)
self.navigationItem.hidesBackButton = true
setupHeader()
setupConstraints()
setupHorizontalBar()
collectionView2.clipsToBounds = true
RoomDeviceListVC.CollectionViewSection = selectedIndex.section
BottomViewRoomCell.delegates = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
// self.collectionView2.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.collectionView2.reloadData()
RoomDeviceListVC.isVisible = true
}
override func viewDidDisappear(_ animated: Bool) {
RoomDeviceListVC.isVisible = false
}
func setupHorizontalBar(){
let horizontalBar = UIView()
self.view.addSubview(horizontalBar)
horizontalBar.translatesAutoresizingMaskIntoConstraints = false
horizontalBar.backgroundColor = .white
horizontalBarLeadingConstraint = horizontalBar.leadingAnchor.constraint(equalTo: collectionView2.leadingAnchor)
horizontalBarLeadingConstraint?.isActive = true
horizontalBar.heightAnchor.constraint(equalToConstant: 3).isActive = true
NSLayoutConstraint(item: horizontalBar, attribute: .width, relatedBy: .equal, toItem: collectionView1, attribute: .width, multiplier: 0.5, constant: 1).isActive = true
horizontalBar.bottomAnchor.constraint(equalTo: collectionView2.topAnchor).isActive = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let menuName = ["Rooms" , "DeviceList"]
let color :[UIColor] = [.green , .blue]
if collectionView == collectionView1{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuBarCell", for: indexPath) as! MenuBarCell
cell.menuLabel.text = menuName[indexPath.section]
cell.menuLabel.textColor = UIColor.darkGray
DispatchQueue.main.async {
self.collectionView1.selectItem(at: self.selectedIndex, animated: false, scrollPosition: .centeredHorizontally)
}
return cell
}
else {
if indexPath.section == 0{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BottomViewRoomCell", for: indexPath) as! BottomViewRoomCell
cell.backgroundColor = .clear
cell.layer.cornerRadius = 9
cell.layer.masksToBounds = true
return cell
}
else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BottomViewDeviceCell", for: indexPath) as! BottomViewDeviceCell
cell.backgroundColor = .clear
cell.layer.cornerRadius = 9
cell.layer.masksToBounds = true
return cell
}
}
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
if collectionView == collectionView1{
return CGSize(width: self.view.frame.width / 2, height: collectionView1.frame.height)
}
else{
return CGSize(width: self.view.frame.width, height: collectionView2.frame.height)
}
}

you may use UINavigationControllerDelegate to handle this situation.
add UINavigationControllerDelegate, sign self to the delegate of your collectionView's navigationController, and then put your personal function inside "willShow/didShow" delegate method. your personal function will be called when you pop back to the collectionView.
some method about cell display is not shown
class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UINavigationControllerDelegate{
private let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
// sign navigationController's delegate to collectionViewController itself.
navigationController?.delegate = self
}
// push view controller when cell is selected
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = CustomViewController()
navigationController?.pushViewController(vc, animated: true)
}
// call your personal function when this collection view controller shows back
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if viewController == self {
print("will show self")
}
}
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if viewController == self {
print("did show self")
}
}
}

Related

Swift cells are created but don't show up in UICollectionView swift 5

I'm creating a collection view with custom collection view cell. So, I've created the collection view in my storyboard view controller. The problem is that my cells are created in cellForItemAt but they are not shown at all, I've also tried using default cells, but they are not shown either, and of course, I've tried all the solutions I found. Thank you for helping in advance!
view controller class related parts
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
title = set?.name
collectionView!.register(WordCollectionViewCell.self, forCellWithReuseIdentifier: Identifies.WordCollectionViewCellIdentifier)
let buttonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addButtonPressed))
navigationItem.rightBarButtonItem = buttonItem
updateTranslations()
}
hide: false console: true babel: false -->
extension SingleSetViewController : UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(translations.count)
return translations.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Identifies.WordCollectionViewCellIdentifier, for: indexPath) as! WordCollectionViewCell
cell.translation = translations[indexPath.row]
return cell
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
}
collection view cell class
class WordCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var wordLabel: UILabel!
private var showsWord = true
private var _translation : Translation?
var translation : Translation {
set {
_translation = newValue
}
get {
return _translation ?? Translation(word: "", translation: "")
}
}
override func awakeFromNib() {
super.awakeFromNib()
let gestureRecognizer = UITapGestureRecognizer(target: self , action: #selector(flip))
gestureRecognizer.numberOfTapsRequired = 1
self.addGestureRecognizer(gestureRecognizer)
wordLabel.text = _translation?.word
}
#objc private func flip(_ sender: UIGestureRecognizer){
let options : UIView.AnimationOptions = [.transitionFlipFromRight]
UIView.transition(with: self, duration: 0.5, options: options){
if let translation = self._translation {
self.wordLabel.text = self.showsWord ? translation.translation : translation.word
self.showsWord = !self.showsWord
}
}
}
static func nib() -> UINib {
return UINib(nibName: NibNames.WordCollectionViewCellNibName, bundle: nil)
}
}
your awakeFromNib method is probably firing before your set your cell's translation property. Try setting your label text in your translation's setter:
set {
_translation = newValue
wordLabel?.text = _translation?.word
}

Reliably track Page Index in a UIPageViewController (Swift)

The problem:
I have a master UIPageViewController (MainPageVC) with three imbedded page views (A, B, & C) that are accessible both with swipe gestures and by pressing the appropriate locations in a custom page indicator* in the MainPageVC (*not a true UIPageControl but comprised of three ToggleButtons - a simple reimplementation of UIButton to become a toggle-button). My setup is as follows:
Previous reading:
Reliable way to track Page Index in a UIPageViewController - Swift, A reliable way to get UIPageViewController current index, and UIPageViewController: return the current visible view
indicated that the best way to do this was with didFinishAnimating calls, and manually keep track of the current page index, but I'm finding that this does not deal with certain edge cases.
I have been trying to produce a safe way of keeping track of the current page index (with didFinishAnimating and willTransitionTo methods) but am having trouble with the edge case where a user is in view A, and then swipes all the way across to C (without lifting up their finger), and then beyond C, and then releasing their finger... in this instance didFinishAnimating isn't called and the app still believes it is in A (i.e. A toggle button is still pressed and pageIndex is not updated correctly by the viewControllerBefore and viewControllerAfter methods).
My code:
#IBOutlet weak var pagerView: UIView!
#IBOutlet weak var aButton: ToggleButton!
#IBOutlet weak var bButton: ToggleButton!
#IBOutlet weak var cButton: ToggleButton!
let viewControllerNames = ["aVC", "bVC", "cVC"]
lazy var buttonsArray = {
[aButton, bButton, cButton]
}()
var previousPage = "aVC"
var pageVC: UIPageViewController?
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
print("TESTING - will transition to")
let currentViewControllerClass = String(describing: pageViewController.viewControllers![0].classForCoder);
let viewControllerIndex = viewControllerNames.index(of: currentViewControllerClass);
if currentViewControllerClass == previousPage {
return
}
let pastIndex = viewControllerNames.index(of: previousPage)
if buttonsArray[pastIndex!]?.isOn == true {
buttonsArray[pastIndex!]?.buttonPressed()
}
if let newPageButton = buttonsArray[viewControllerIndex!] {
newPageButton.buttonPressed()
}
self.previousPage = currentViewControllerClass
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
print("TESTING - did finish animating")
let currentViewControllerClass = String(describing: pageViewController.viewControllers![0].classForCoder)
let viewControllerIndex = viewControllerNames.index(of: currentViewControllerClass)
if currentViewControllerClass == previousPage {
return
}
let pastIndex = viewControllerNames.index(of: previousPage)
if buttonsArray[pastIndex!]?.isOn == true {
buttonsArray[pastIndex!]?.buttonPressed()
}
if let newPageButton = buttonsArray[viewControllerIndex!] {
newPageButton.buttonPressed()
}
self.previousPage = currentViewControllerClass
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let onboardingViewControllerClass = String(describing: viewController.classForCoder)
let viewControllerIndex = viewControllerNames.index(of: onboardingViewControllerClass)
let newViewControllerIndex = viewControllerIndex! - 1
if(newViewControllerIndex < 0) {
return nil
} else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: viewControllerNames[newViewControllerIndex])
if let vc = vc as? BaseTabVC {
vc.mainPageVC = self
vc.intendedCollectionViewHeight = pagerViewHeight
}
return vc
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let onboardingViewControllerClass = String(describing: viewController.classForCoder)
let viewControllerIndex = viewControllerNames.index(of: onboardingViewControllerClass)
let newViewControllerIndex = viewControllerIndex! + 1
if(newViewControllerIndex > viewControllerNames.count - 1) {
return nil
} else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: viewControllerNames[newViewControllerIndex])
if let vc = vc as? BaseTabVC {
vc.mainPageVC = self
vc.intendedCollectionViewHeight = pagerViewHeight
}
return vc
}
}
I'm at a loss as to how to deal with this edge case, the problem is that it can lead to fatal crashes of the app if the user then tries to press something in C that should otherwise be guaranteed to exist, and an unexpected nil or indexOutOfBounds error is thrown.
Very well written question. Especially for a newbie. (Voted.) You clearly state the problem you're having, including illustrations and your current code.
The solution I proposed in another thread was to subclass UIPageControl and have it implement a didSet on its currentPage property. You can then have the page control notify the view controller of the current page index. (By giving your custom subclass a delegate property, by sending a notification center message, or whatever method best fits your needs.)
(I did a simple test of this approach and it worked. I didn't test exhaustively however.)
The fact that the UIPageViewController reliably updates the page control but that there's no reliable, obvious way to figure out the current page index seems like an oversight in the design of this class.
Own Solution
I found the solution to this: don't use a UIPageView(Controller), use a CollectionView(Controller) instead. It is MUCH easier to keep track of the position of a collection view than to try and manually keep track of the current page in a UIPageViewController.
The solution is as follows:
Method
Refactor MainPagerVC as a CollectionView(Controller) (or as a regular VC that conforms to the UICollectionViewDelegate UICollectionViewDataSource protocols).
Set each page (aVC, bVC, and cVC) as a UICollectionViewCell subclass (MainCell).
Set each of these pages to fill the MainPagerVC.collectionView within the screen's bounds - CGSize(width: view.frame.width, height: collectionView.bounds.height).
Refactor the toggle-buttons at the top (A, B, and C) as three UICollectionViewCell subclasses (MenuCell) in a MenuController (itself a UICollectionViewController.
As collection views inherit from UIScrollView you can implement scrollViewDidScroll, scrollViewDidEndScrollingAnimation and scrollViewWillEndDragging methods, along with delegation (with didSelectItemAt indexPath) to couple the MainPagerVC and MenuController collection views.
Code
class MainPagerVC: UIViewController, UICollectionViewDelegateFlowLayout {
fileprivate let menuController = MenuVC(collectionViewLayout: UICollectionViewFlowLayout())
fileprivate let cellId = "cellId"
fileprivate let pages = ["aVC", "bVC", "cVC"]
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .white
cv.showsVerticalScrollIndicator = false
cv.showsHorizontalScrollIndicator = false
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
menuController.delegate = self
setupLayout()
}
fileprivate func setupLayout() {
guard let menuView = menuController.view else { return }
view.addSubview(menuView)
view.addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
//Setup constraints (placing the menuView above the collectionView
collectionView.register(MainCell.self, forCellWithReuseIdentifier: cellId)
//Make the collection view behave like a pager view (no overscroll, paging enabled)
collectionView.isPagingEnabled = true
collectionView.bounces = false
collectionView.allowsSelection = true
menuController.collectionView.selectItem(at: [0, 0], animated: true, scrollPosition: .centeredHorizontally)
}
}
extension MainPagerVC: MenuVCDelegate {
// Delegate method implementation (scroll to the right page when the corresponding Menu "Button"(Item) is pressed
func didTapMenuItem(indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
extension MainPagerVC: UICollectionViewDelegate, UICollectionViewDataSource {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let x = scrollView.contentOffset.x
let offset = x / pages.count
menuController.menuBar.transform = CGAffineTransform(translationX: offset, y: 0)
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
let item = Int(scrollView.contentOffset.x / view.frame.width)
let indexPath = IndexPath(item: item, section: 0)
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .bottom)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let x = targetContentOffset.pointee.x
let item = Int(x / view.frame.width)
let indexPath = IndexPath(item: item, section: 0)
menuController.collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return pages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MainCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: view.frame.width, height: collectionView.bounds.height)
}
}
class MainCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
// Custom UIColor extension to return a random colour (to check that everything is working)
backgroundColor = UIColor().random()
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
protocol MenuVCDelegate {
func didTapMenuItem(indexPath: IndexPath)
}
class MenuVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
fileprivate let cellId = "cellId"
fileprivate let menuItems = ["A", "B", "C"]
var delegate: MenuVCDelegate?
//Sliding bar indicator (slightly different from original question - like Reddit)
let menuBar: UIView = {
let v = UIView()
v.backgroundColor = .red
return v
}()
//1px view to visually separate MenuBar region from "pager"-views
let menuSeparator: UIView = {
let v = UIView()
v.backgroundColor = .gray
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = .white
collectionView.allowsSelection = true
collectionView.register(MenuCell.self, forCellWithReuseIdentifier: cellId)
if let layout = collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
}
//Add views and setup constraints for collection view, separator view and "selection indicator" view - the menuBar
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.didTapMenuItem(indexPath: indexPath)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuItems.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MenuCell
cell.label.text = menuItems[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.frame.width
return .init(width: width/CGFloat(menuItems.count), height: view.frame.height)
}
}
class MenuCell: UICollectionViewCell {
let label: UILabel = {
let l = UILabel()
l.text = "Menu Item"
l.textAlignment = .center
l.textColor = .gray
return l
}()
override var isSelected: Bool {
didSet {
label.textColor = isSelected ? .black : .gray
}
}
override init(frame: CGRect) {
super.init(frame: frame)
//Add label to view and setup constraints to fill Cell
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
References
A "Lets Build That App" YouTube Video: "We Made It on /r/iosprogramming! Live coding swiping pages feature"

Want to push to another view controller from a container view

What do i have: A view controller (WhereViewController) with some "child/container"-views. One is a collection view (typeCollectionView). the WhereViewController is embedded in a navigationController:
Screenshot
class WhereViewController: UIViewController {
lazy var progressContainerView: ProgressBarView = {
let containerView = ProgressBarView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
let questionLabel: UILabel = {
let label = UILabel()
label.text = "question?"
label.textAlignment = NSTextAlignment.center
label.font = UIFont.init(name: "OpenSans-Italic", size: 14)
label.textColor = UIColor(red:0.33, green:0.33, blue:0.33, alpha:1.0)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let typeCollectionView: WhereCollectionView = {
let collectionView = WhereCollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Schaden melden"
view.backgroundColor = UIColor.white
view.addSubview(progressContainerView)
view.addSubview(questionLabel)
view.addSubview(typeCollectionView)
setupProgressContainerView()
setupQuestionLabel()
setupTypeCollectionView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func setupProgressContainerView() {
progressContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 60).isActive = true
progressContainerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
progressContainerView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
progressContainerView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
private func setupQuestionLabel() {
questionLabel.topAnchor.constraint(equalTo: progressContainerView.bottomAnchor, constant: 22).isActive = true
questionLabel.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
questionLabel.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
questionLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
}
private func setupTypeCollectionView() {
typeCollectionView.topAnchor.constraint(equalTo: questionLabel.bottomAnchor, constant: 20).isActive = true
typeCollectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
typeCollectionView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
typeCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
Want am I trying to do: Within the typeCollectionView: If an item is selected (didSelectItemAt) i want to push to the next viewController via navigationController of the WhereViewController. My typeCollectionView looks like:
class WhereCollectionView: UICollectionView, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var whereViewController: WhereViewController?
let label = ["x", "y", "z"]
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
let layout = UICollectionViewFlowLayout()
super.init(frame: CGRect.zero, collectionViewLayout: layout)
backgroundColor = UIColor.white
delegate = self
dataSource = self
self.register(WhereCollectionViewCell.self, forCellWithReuseIdentifier: "WhereCollectionViewCell")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return label.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WhereCollectionViewCell", for: indexPath) as! WhereCollectionViewCell
cell.injureLabel.text = label[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.frame.width, height: 75)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
}
I've tried to give the whereViewController to the typeCollectionView:
let typeCollectionView: WhereCollectionView = {
let collectionView = WhereCollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.whereViewController = self
return tableView
}()
But get: Cannot assign value of type '(NSObject) -> () -> WhereViewController' to type 'WhereViewController?'
Could someone help me? or isn't it possible to "speak" with the navigationController from the collectionView (containerView)?
I do not use storyboard
Try to:
1) Add extension:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if parentResponder is UIViewController {
return parentResponder as! UIViewController!
}
}
return nil
}
}
2) Now you can use:
parentViewController?.present(<ControllerName>, animated: true)
Hope it helps
You should conform the view controller to the collection delegate methods and not the collection view itself.
Make WhereViewController conform to UICollectionViewDataSource, UICollectionViewDelegate and UICollectionViewDelegateFlowLayout:
class WhereViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
In WhereViewController's viewDidLoad method, set up the delegate and the data source:
override func viewDidLoad() {
super.viewDidLoad()
typeCollectionView.dataSource = self
typeCollectionView.delegate = self
[...]
}
Here you can use delegate to send message from CollectionView to
ViewController.
Simple declare a protocol
protocol CustomDelegate: class {
func collectionView(DidSelect : Int)
}
After that make an instance of CustomDelegate protocol in WhereCollectionView Class
class WhereCollectionView: UICollectionView {
weak var customDelegate:CustomDelegate?
}
and in didSelect methods fire the delegate method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let _del = customDelegate {
_del.collectionView(DidSelect : IndexPath.row)
}
}
In ViewController there you are adding collectionview in viewcontroller set delegate to self
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Schaden melden"
view.backgroundColor = UIColor.white
typeCollectionView.customDelegate = self
view.addSubview(progressContainerView)
view.addSubview(questionLabel)
view.addSubview(typeCollectionView)
setupProgressContainerView()
setupQuestionLabel()
setupTypeCollectionView()
}
Also Implement the Custom delegate method in ViewController
func collectionView(DidSelect : Int) {
/// from there you can do your job
}

CollectionView not registering cells

I have a viewController with a collectionView inside of it. I'm pretty sure that I have configured everything right yet it is not rendering any cells. I have added the appropriate delegates and data sources and double checked to see if the data is loading and it is but cells are not being populated
import UIKit
import UIKit
import Alamofire
import AlamofireNetworkActivityIndicator
import SwiftLocation
import CoreLocation
import AMScrollingNavbar
class NewHomeFeedControllerViewController: UIViewController {
let detailView = EventDetailViewController()
var allEvents = [Event]()
let customCellIdentifier1 = "customCellIdentifier1"
var grideLayout = GridLayout(numberOfColumns: 2)
let refreshControl = UIRefreshControl()
var newHomeFeed: NewHomeFeedControllerViewController?
let paginationHelper = PaginationHelper<Event>(serviceMethod: PostService.showEvent)
lazy var dropDownLauncer : DropDownLauncher = {
let launcer = DropDownLauncher()
launcer.newHomeFeed = self
return launcer
}()
// 1 IGListKit uses IGListCollectionView, which is a subclass of UICollectionView, which patches some functionality and prevents others.
let collectionView: UICollectionView = {
// 2 This starts with a zero-sized rect since the view isn’t created yet. It uses the UICollectionViewFlowLayout just as the ClassicFeedViewController did.
let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
// 3 The background color is set to white
view.backgroundColor = UIColor.white
return view
}()
func handleDropDownMenu(){
dropDownLauncer.showDropDown()
}
func configureCollectionView() {
// add pull to refresh
refreshControl.addTarget(self, action: #selector(reloadHomeFeed), for: .valueChanged)
collectionView.addSubview(refreshControl)
}
func reloadHomeFeed() {
self.paginationHelper.reloadData(completion: { [unowned self] (events) in
self.allEvents = events
if self.refreshControl.isRefreshing {
self.refreshControl.endRefreshing()
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
})
}
func categoryFetch(dropDown: DropDown){
navigationItem.title = dropDown.name
paginationHelper.category = dropDown.name
configureCollectionView()
reloadHomeFeed()
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.contentInset = UIEdgeInsetsMake(15, 0, 0, 0)
navigationItem.title = "Home"
collectionView.dataSource = self
collectionView.delegate = self
collectionView.collectionViewLayout = grideLayout
collectionView.reloadData()
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: customCellIdentifier1)
// self.navigationItem.hidesBackButton = true
let backButton = UIBarButtonItem(image: UIImage(named: "menu"), style: .plain, target: self, action: #selector(handleDropDownMenu))
self.navigationItem.leftBarButtonItem = backButton
configureCollectionView()
reloadHomeFeed()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let navigationController = self.navigationController as? ScrollingNavigationController {
navigationController.followScrollView(self.collectionView, delay: 50.0)
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if let navigationController = navigationController as? ScrollingNavigationController {
navigationController.stopFollowingScrollView()
}
}
func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
if let navigationController = navigationController as? ScrollingNavigationController {
navigationController.showNavbar(animated: true)
}
return true
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
grideLayout.invalidateLayout()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
extension NewHomeFeedControllerViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return allEvents.count
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier1, for: indexPath) as! CustomCell
let imageURL = URL(string: allEvents[indexPath.item].currentEventImage)
print(imageURL ?? "")
customCell.sampleImage.af_setImage(withURL: imageURL!)
return customCell
}
}
extension NewHomeFeedControllerViewController: UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item == 0 || indexPath.item == 1 {
return CGSize(width: view.frame.width, height: grideLayout.itemSize.height)
}else{
return grideLayout.itemSize
}
}
}
Any idea what could be going on?
Sorry you guys forgot to add this
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
Haha thanks for the help

create UICollectionViewController subclass and add as a childviewcontroller

I am new to iOS development. I want to create collectionView in two different view controllers with same UI.I want to create only one UICollectionView and resuse it on different view controller instead of create separate collectionViews . On approach i can follow is to create the UICollectionViewController subclass and add this on my viewcontrollers as a childviewcontroller, but not sure if this is the correct approach do not know how addChildViewcontroller works and how to pass data between child and parent viewcontrollers. It would be great if someone can help on this. If any sample code is available to achive this please let me know.
Any help is much appreciated.
You can pass around the same collection view controller instance. Add it in viewWillAppear and remove in viewDidDisappear in first and second classes. Here is a sample code that you could use,
extension UIColor {
class func randomColor() -> UIColor {
let red = CGFloat(arc4random_uniform(255)) / 255.0
let green = CGFloat(arc4random_uniform(255)) / 255.0
let blue = CGFloat(arc4random_uniform(255)) / 255.0
return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
}
class MyCollectionViewController: UICollectionViewController {
let data: [UIColor]
init(data: [UIColor]) {
self.data = data
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSizeMake(100, 100)
layout.scrollDirection = UICollectionViewScrollDirection.Vertical
super.init(collectionViewLayout: layout)
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath)
cell.backgroundColor = data[indexPath.item]
return cell
}
}
class FirstViewController: UIViewController {
lazy var myData:[UIColor] = {
var allData = [UIColor]()
for i in 0 ..< 20 {
allData.append(UIColor.randomColor())
}
return allData
}()
var collectionViewController: MyCollectionViewController!
override func viewDidLoad() {
super.viewDidLoad()
collectionViewController = MyCollectionViewController(data: self.myData)
let barButton = UIBarButtonItem(title: "Show next", style: .Plain, target: self, action: "showNext:")
navigationItem.rightBarButtonItem = barButton
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let collectionView = collectionViewController.view
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
addChildViewController(collectionViewController)
collectionView.topAnchor.constraintEqualToAnchor(view.topAnchor).active = true
collectionView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor).active = true
collectionView.leftAnchor.constraintEqualToAnchor(view.leftAnchor).active = true
collectionView.rightAnchor.constraintEqualToAnchor(view.rightAnchor).active = true
collectionViewController.didMoveToParentViewController(self)
}
override func viewWillDisappear(animated: Bool) {
super.viewDidDisappear(animated)
collectionViewController.willMoveToParentViewController(nil)
collectionViewController.view.removeFromSuperview()
collectionViewController.removeFromParentViewController()
}
func showNext(sender: AnyObject) {
let secondViewController = SecondViewController(collectionViewController: collectionViewController)
navigationController?.pushViewController(secondViewController, animated: true)
}
}
class SecondViewController: UIViewController {
var collectionViewController: MyCollectionViewController!
init(collectionViewController: MyCollectionViewController) {
self.collectionViewController = collectionViewController
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let collectionView = collectionViewController.view
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
addChildViewController(collectionViewController)
collectionView.topAnchor.constraintEqualToAnchor(view.topAnchor).active = true
collectionView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor).active = true
collectionView.leftAnchor.constraintEqualToAnchor(view.leftAnchor).active = true
collectionView.rightAnchor.constraintEqualToAnchor(view.rightAnchor).active = true
collectionViewController.didMoveToParentViewController(self)
}
override func viewWillDisappear(animated: Bool) {
super.viewDidDisappear(animated)
collectionViewController.willMoveToParentViewController(nil)
collectionViewController.view.removeFromSuperview()
collectionViewController.removeFromParentViewController()
}
}
I have a Set of Answers you can use,
My source Code is ParentViewController and ChildViewController are
same viewController to be declared.
First you create the ParentViewController and add the
UICollectionView then set the Cell size in ParentViewController.
Second you create UICollectionViewCell in same parentViewController,
then add what u need Label or Buttons to be declare.
In ParentViewController class declare 'UICollectionViewDelegate',
Ex: class MyViewController: UIViewController, UICollectionViewDelegate
Then Create UICollectionViewDelegate methods and i have put my
methods below,
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayvalue.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("reuseIdentifier", forIndexPath: indexPath)
// Configure the cell
let baseView = cell.viewWithTag(101)
let titleLabel = baseView?.viewWithTag(102) as! UILabel
titleLabel.text = arrayvalue[indexPath.row] as String
return cell
}
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(CellSize)
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
collectionView.deselectItemAtIndexPath(indexPath, animated:true)
let storyBoard = UIStoryboard(name: "storyboardName", bundle: nil)
let name: classname = storyBoard.instantiateViewControllerWithIdentifier("reuseIdentifier") as! AnotherViewController
self.navigationController?.pushViewController(name, animated: true)
}
Very Important to give storyboard 'reuseIdentifier' value and also
give inside the class cellForItemAtIndexPath reuseIdentifier, example this line
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("reuseIdentifier", forIndexPath: indexPath)
This code was working for me...

Resources