Reputation: 463
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
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
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
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