Abdullah Ajmal
Abdullah Ajmal

Reputation: 463

How to disable specific cells in collection view in Swift

I have created a collection view that acts as a nav bar, and I've made this the base view controller(TabBar.swift) and it will appear on all my other view controllers. it has 3 cells for now and clicking on each cell shall direct the user to its respective view controller. The 3 view Controllers are FirstScreen.swift, SecondScreen.swift and ThirdScreen.swift. Here is the code of the base view controller:

TabBar.swift

struct OpenViewController {
    var viewController: UIViewController
}

class TabBar: UICollectionViewController, UICollectionViewDelegateFlowLayout {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Call all the elements
        setupCollectionView()

        view.backgroundColor = .white


    }

    let viewControllerList = [

        OpenViewController(viewController: FirstScreen()),
        OpenViewController(viewController: SecondScreen()),
        OpenViewController(viewController: ThirdScreen()),

    ]

    // Setup the collection veiw
    func setupCollectionView() {

        collectionView?.backgroundColor = .clear
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellID")
        collectionView.showsHorizontalScrollIndicator = false

        view.addSubview(collectionView)
        addCollectionViewConstraints()

    }

    // Add constraints to the collection view
    func addCollectionViewConstraints() {

        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.3).isActive = true

    }

    // The number of elements inside the collection view
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 3
    }

    // Adding the cells
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath)

        // Customize the cells
        cell.backgroundColor = .clear
        cell.layer.cornerRadius = collectionView.contentSize.height / 2
        cell.layer.borderWidth = 1
        cell.layer.borderColor = UIColor.black.cgColor

        return cell
    }

    // The witdth and the height of the cell
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.height, height: collectionView.frame.height)
    }

    // Padding to the cells
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
    }

    // Selecting a cell and navigating to another view controller
    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        let openViewController = self.viewControllerList[indexPath.row]
        self.present(openViewController.viewController, animated: true)
    }

}

FirstScreen.swift

class FirstScreen: TabBar {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .yellow

    }
}

SecondScreen.swift

class SecondScreen: TabBar {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .green

    }
}

ThirdScreen.swift

class ThirdScreen: TabBar {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .red

    }
}

AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    window = UIWindow()
    window?.makeKeyAndVisible()

    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    let firstScreen = SecondScreen(collectionViewLayout: layout)

    window?.rootViewController = firstScreen

    return true
}

Now when I run this, this crashes and the reason is, for example, if the user is on FirstScreen.swift and the user clicks on the first cell which will lead the user to the FirstScreen.swift it will not work, is there a way to solve this problem? Like to disable a cell when the user is on the respective view controller.

Upvotes: 0

Views: 1823

Answers (3)

Terry
Terry

Reputation: 108

I copied and ran your code, then I got the crash at this part

 let viewControllerList = [

        OpenViewController(viewController: FirstScreen()),
        OpenViewController(viewController: SecondScreen()),
        OpenViewController(viewController: ThirdScreen()),

 ]

I think there is a deadloop when you try to create the SecondScreen in AppDelegate, the root TabBar of this class will create again the SecondScreen, then it will repeat this step again and again...

You should set the viewControllerList empty at the beginning, then reassign the list in the viewDidLoad of TabBar.

    override func viewDidLoad() {
        super.viewDidLoad()

        // Call all the elements
        setupCollectionView()

        view.backgroundColor = .white

        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        viewControllerList = [
            OpenViewController(viewController: FirstScreen(collectionViewLayout: layout)),
            OpenViewController(viewController: SecondScreen(collectionViewLayout: layout)),
            OpenViewController(viewController: ThirdScreen(collectionViewLayout: layout)),
        ]
    }

    var viewControllerList: [OpenViewController] = []

This will also resolve the crash you had (I suppose this is the crash you've faced). Because you didn't initialise any layout of the collectionView when you create these classes. Just do it as you did in the AppDelegate

SecondScreen(collectionViewLayout: layout)

Hope it could help you.

Upvotes: 0

henrik-dmg
henrik-dmg

Reputation: 1493

I cannot try this code myself right now but adding something like this in collectionView(_ collectionView: UICollectionView, didSelectItemAt...) should work:

let openViewController = self.viewControllerList[indexPath.row]

if type(of: self) != type(of: secondViewController) {
    self.present(openViewController.viewController, animated: true)
}

The easy way would be a global variable to track which index is selected but that's not a very safe and nice way to do it :P

Upvotes: 0

Shehata Gamal
Shehata Gamal

Reputation: 100503

The problem is that you're trying to present a vc that's currently presented , so You can do

var current:Int?   

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard current != indexPath.row else { return }
    let openViewController = self.viewControllerList[indexPath.row]
    self.present(openViewController.viewController, animated: true)
    current = indexPath.row
}

BTW i think this best for a UISegmentedControl

Upvotes: 1

Related Questions