to dismiss a viewcontroller using collectionView cell - swift

i am having a difficulty on dismissing a viewController when i click a collectionviewcell. so my collectionview is placed inside a uiview that is then displayed in a viewcontroller. whenever the user click on collectionViewCell, i want the view to trigger bye() function that is placed inside the viewcontroller. i add print("bye") just to see if it work, and it does print the word but it does not execute dismiss(animated: true, completion: nil) to dismiss the viewcontroller along with the uiview and collectionview. why it does not dismiss the controller? is there another way that i can do the same thing? here is the code :
the view controller
class sideViewController: UIViewController {
let dismissBtn:UIButton = {
let content = UIButton()
content.backgroundColor = .green
content.addTarget(self, action: #selector(bye), for: .touchUpInside)
return content
}()
let sideTableViews: sideCollectionView = {
let content = sideCollectionView()
return content
}()
override func viewDidLoad() {
super.viewDidLoad()
dismissBtn.translatesAutoresizingMaskIntoConstraints = false
sideTableViews.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dismissBtn)
view.addSubview(sideTableViews)
dismissBtn.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
dismissBtn.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10).isActive = true
dismissBtn.widthAnchor.constraint(equalToConstant:40).isActive = true
dismissBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
sideTableViews.topAnchor.constraint(equalTo: dismissBtn.bottomAnchor, constant: 30).isActive = true
sideTableViews.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
sideTableViews.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
sideTableViews.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
#objc func membershipController(){
let next = self.storyboard?.instantiateViewController(withIdentifier: "membershipViewController") as! membershipViewController
self.present(next, animated: true, completion: nil)
}
#objc func bye(){
print("bye")
dismiss(animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
this is the collectionView code and the uiview
class sideCollectionView:UIView, UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {
let arrayLbl = ["connection","achievement","template","setting"]
let arrayImg = ["connection","achievement","template","setting"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayLbl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! sideCollectionViewCell
cell.titleImg.image = UIImage(named: "\(arrayImg[indexPath.row])")
cell.titleLbl.text = arrayLbl[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (self.frame.width / 2) - 40, height: (self.frame.width / 2) - 40)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(25, 25, 10, 25)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0{
connectController()
print("connection")
let side = sideViewController()
side.bye()
}
if indexPath.row == 1{
let side = sideViewController()
side.bye()
print("achievement")
}
if indexPath.row == 2{
let side = sideViewController()
side.bye()
print("template")
}
if indexPath.row == 3{
let side = sideViewController()
side.bye()
print("setting")
}
}
lazy var collectionViews: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.clear
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews(){
collectionViews.register(sideCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionViews.translatesAutoresizingMaskIntoConstraints = false
backgroundColor = UIColor.clear
addSubview(collectionViews)
collectionViews.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
collectionViews.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
collectionViews.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
collectionViews.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0).isActive = true
collectionViews.widthAnchor.constraint(equalTo: widthAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func connectController(){
let side = sideViewController()
side.bye()
}
#objc func settingController(){
let side = sideViewController()
side.bye()
}
#objc func achievementController(){
let side = sideViewController()
side.bye()
}
#objc func templateController(){
let side = sideViewController()
side.bye()
}
}

Because this
let side = sideViewController()
is another instance other than the displayed one , so add this var to the view
class sideCollectionView:UIView {
var currentVc:sideViewController?
}
//
when you create the variable assign it
lazy var sideTableViews: sideCollectionView = {
let content = sideCollectionView()
content.currentVc = self
return content
}()
//
Inside
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0{
connectController()
print("connection")
currentVc?.bye()
}
}

Related

How to scroll to a given indexPath.item?

I have 2 collection views in parallel. One of them is a menu and the other is a big one containing vertical table views that are populated with API queried data.
When the user scrolls right, then Order History is highlighted and the next cell in the big collection view at the bottom takes over the rest of the screen and it shows its own tableview data. It's just like the implementation here.
Everything works EXCEPT when I tap on Order History in the state indicated in the attached image, the collection view at the bottom does not scroll to the 2nd cell in the big collection view. If I tap on the Current Orders cell then it does scroll to the 1st cell in the big collection view. I have no idea what's causing this problem after spending the last 48 hours on this.
This is the menu bar:
class MenuBar: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
lazy var menuCollection: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionview = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionview.translatesAutoresizingMaskIntoConstraints = false
collectionview.backgroundColor = .white
collectionview.delegate = self
collectionview.dataSource = self
collectionview.register(InvoiceCell.self, forCellWithReuseIdentifier: InvoiceCell.identifier)
return collectionview
}()
var leftIndicatorConstraint: NSLayoutConstraint?
private let menuOptions = ["Current Orders", "Order History"]
var containerViewController: ContainerViewController? // I use an instance of the parent VC to pass on the value
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(menuCollection)
menuCollection.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
menuCollection.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
menuCollection.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
menuCollection.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
menuCollection.selectItem(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .left)
setupIndicator()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupIndicator() {
let indicatorBar = UIView()
indicatorBar.backgroundColor = UIColor(hex: Constants.Colors.primary)
addSubview(indicatorBar)
indicatorBar.translatesAutoresizingMaskIntoConstraints = false
leftIndicatorConstraint = indicatorBar.leadingAnchor.constraint(equalTo: self.leadingAnchor)
leftIndicatorConstraint?.isActive = true
indicatorBar.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
indicatorBar.heightAnchor.constraint(equalToConstant: 5).isActive = true
indicatorBar.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5).isActive = true
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
containerViewController?.scrollToMenu(menuIndex: indexPath.item) // Here I pass on the value of the menu item that's tapped
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menuOptions.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InvoiceCell.identifier, for: indexPath) as? InvoiceCell else { return UICollectionViewCell() }
cell.thisOption = menuOptions[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: menuCollection.frame.size.width / CGFloat(menuOptions.count), height: menuCollection.frame.size.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
}
}
And this is the parent UIView Controller that contains the menu collection view as well as the big base collection view
class ContainerViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
lazy var menuBar: MenuBar = {
let menuBar = MenuBar()
menuBar.containerViewController = self
menuBar.translatesAutoresizingMaskIntoConstraints = false
return menuBar
}()
lazy var baseCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(PastCell.self, forCellWithReuseIdentifier: PastCell.pastCellId)
collectionView.register(CurrentCell.self, forCellWithReuseIdentifier: CurrentCell.currentCellId)
collectionView.isPagingEnabled = true
collectionView.delegate = self
collectionView.dataSource = self
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.backgroundColor = .white
return collectionView
}()
var orderViewModel = OrderViewModel(order: Order(addressOne: "", addressTwo: "", city: "", postalCode: "", mpName: "", planType: 0, restaurantAddress: "", restaurantId: "", restaurantName: "", timeOfCreation: nil, currentTotal: 0.00, orderMenu: [], status: 0, itemsHTML: ""))
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupView()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
orderViewModel.removeListener()
}
private func setupView() {
view.addSubview(menuBar)
menuBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
menuBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
menuBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
menuBar.heightAnchor.constraint(equalToConstant: 40).isActive = true
view.addSubview(baseCollectionView)
baseCollectionView.topAnchor.constraint(equalTo: menuBar.bottomAnchor, constant: 0).isActive = true
baseCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
baseCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
baseCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
func scrollToMenu(menuIndex: Int) {
// Here I am using the passed on menu index to scroll to the appropriate cell in the big collection view.
let indexPath = IndexPath(item: menuIndex, section: 0)
baseCollectionView.scrollToItem(at: indexPath, at: .left, animated: true) // For some reason it ALWAYS SCROLL TO THE FIRST CELL }
func scrollViewDidScroll(_ scrollView: UIScrollView) {
menuBar.leftIndicatorConstraint?.constant = scrollView.contentOffset.x / 2
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let index = Int(targetContentOffset.pointee.x / view.frame.width)
menuBar.menuCollection.selectItem(at: IndexPath(item: index, section: 0), animated: true, scrollPosition: .left)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.row {
case 0:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CurrentCell.currentCellId, for: indexPath) as? CurrentCell {
if let tabBarHeight = self.tabBarController?.tabBar.frame.size.height {
cell.adjustSize(top: menuBar.frame.height * 1.2, bottom: tabBarHeight * 0.5)
return cell
}
}
case 1:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PastCell.pastCellId, for: indexPath) as? PastCell {
cell.backgroundColor = .systemOrange
return cell
}
default:
return UICollectionViewCell()
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.size.width, height: view.frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
Turns out there's a big bug with scrollToItem that Apple hasn't fixed. In my case it's caused by isPagingEnabled. So the best solution I found was here where you disable paging before scrolling to item and then enabling it right again.

how to prevent delay when trying to animate invisible cells in collectionview

I would like to animate all of my collectionview cells and this simplified example illustrates the problem. The problem is that if I scroll down while animating cells the ones that are not visible gets animated with a delay. I would like invisible cells to just be in the final state of animation instead of having animation delay.
In the video you see that when I scroll to the bottom, the final cells starts to animate with delay.
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var collectionview: UICollectionView!
var moveText: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
collectionview = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionview.translatesAutoresizingMaskIntoConstraints = false
collectionview.dataSource = self
collectionview.delegate = self
collectionview.backgroundColor = .systemBackground
collectionview.register(MyCell.self, forCellWithReuseIdentifier: "cell")
view.addSubview(collectionview)
NSLayoutConstraint.activate([
collectionview.topAnchor.constraint(equalTo: view.topAnchor),
collectionview.bottomAnchor.constraint(equalTo: view.bottomAnchor),
collectionview.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionview.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
UIView.animate(withDuration: 1, delay: 0, options: [
UIView.AnimationOptions.allowAnimatedContent,
UIView.AnimationOptions.allowUserInteraction,
]) {
cell.moveText = self.moveText
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: collectionview.frame.width, height: 120)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { timer in
print("Starting animatings")
self.moveText.toggle()
self.collectionview.reloadData()
}
}
}
class MyCell: UICollectionViewCell {
let label = UILabel()
var moveText: Bool = false {
didSet {
if moveText == true {
label.transform = CGAffineTransform(translationX: 50, y: 50)
} else {
label.transform = .identity
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
label.text = "mytext"
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: leadingAnchor),
label.topAnchor.constraint(equalTo: topAnchor)
])
backgroundColor = .orange
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
instead of animating inside the cellForItemAt add this helper function.
private func animateVisibleCells() {
collectionview.visibleCells.compactMap { $0 as? MyCell }.forEach { cell in
UIView.animate(withDuration: 1, delay: 0, options: [
UIView.AnimationOptions.allowAnimatedContent,
UIView.AnimationOptions.allowUserInteraction,
]) {
cell.moveText = self.moveText
}
}
}
Now call it instead of reloading the collection view
Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { timer in
print("Starting animatings")
self.moveText.toggle()
self.animateVisibleCells()
}
and convert the cellForItemAt implementation to:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
cell.moveText = moveText
return cell
}

Button in custom UICollectionViewCell class does not taking action

Hi I am developing a route finder application, and inside the application when user searches for specific places, app provides number of places on both map and in UICollectionView which contains short description of list of places found and a "Go" UIButton to show the direction. However, despite the fact that button is shown on the cell and is touchable, meaning isUserInteractionEnabled is true, button is not triggering an action. "Pressed" is not shown on console. (Note: Screen shot is available for demonstration purposes.)
Declaration of the UICollectionView in MapViewController.
let collectionViewOfListOfPlaces:UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
return cv
}()
In viewDidLoad function of MapViewController this lines are added.
collectionViewOfListOfPlaces.reloadData()
collectionViewOfListOfPlaces.delegate = self
In extension of MapViewController this lines functions are added.
func setupCollectionViewOfListOfPlaces(){
hideListViewButton.translatesAutoresizingMaskIntoConstraints = false
hideListViewButton.isUserInteractionEnabled = true
collectionViewOfListOfPlaces.backgroundColor = UIColor.white.withAlphaComponent(0)
collectionViewOfListOfPlaces.topAnchor.constraint(equalTo: listContentView.topAnchor, constant: 0).isActive = true
collectionViewOfListOfPlaces.leadingAnchor.constraint(equalTo: listContentView.leadingAnchor, constant: 10).isActive = true
collectionViewOfListOfPlaces.trailingAnchor.constraint(equalTo: listContentView.trailingAnchor, constant: -10).isActive = true
collectionViewOfListOfPlaces.heightAnchor.constraint(equalToConstant: view.frame.height/5).isActive = true // ?
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionViewOfListOfPlaces.frame.height/0.8, height: collectionViewOfListOfPlaces.frame.height/1.2)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return landmarks.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionViewOfListOfPlaces.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCell
let cellData = self.landmarks[indexPath.item]
cell.backgroundColor = Colors.violet3
cell.setData(dataa: cellData) //????????
cell.delegate = self
return cell
}
Custom class:
import Foundation
import UIKit
protocol CustomCellDelegate {
func didPrintNameOfPlace(placeName: String)
}
class CustomCell: UICollectionViewCell {
// var data: Landmark? {
// didSet {
// guard let data = data else { return }
//
// }
// }
let directButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Go", for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 18)
button.addTarget(
self,
action: #selector(directButtonPressed),
for: UIControl.Event.touchUpInside)
button.backgroundColor = .white
return button
}()
var data: Landmark?
var delegate: CustomCellDelegate?
override init(frame: CGRect) {
super.init(frame: .zero)
setUpDirectButton()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setData(dataa: Landmark){
data = dataa
}
func setUpDirectButton(){
contentView.addSubview(directButton)
directButton.translatesAutoresizingMaskIntoConstraints = false
directButton.isUserInteractionEnabled = true
directButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
directButton.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0).isActive = true
directButton.widthAnchor.constraint(equalToConstant: 50).isActive = true
directButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
directButton.frame.size.width = 50
directButton.frame.size.height = 50
}
#objc func directButtonPressed(sender: UIButton!) {
// delegate?.didPrintNameOfPlace(placeName: data!.nameOfPlace)
print("Pressed")
}
}
```[Screen shot link][1]
[1]: https://i.stack.imgur.com/azHSd.jpg
Mark your button initialise with lazy
private lazy var directButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Go", for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 18)
button.addTarget(
self,
action: #selector(directButtonPressed),
for: UIControl.Event.touchUpInside)
button.backgroundColor = .white
return button
}()
Cut this line:
button.addTarget(
self,
action: #selector(directButtonPressed),
for: UIControl.Event.touchUpInside)
...and paste it inside setupDirectButton. Your button will start working.

How can i working push view controller on swift 5?

Didselect function in CollectionView or TableView;
let trial = TrialViewController ()
self.navigationController?.pushViewController (trial, animated: true)
I want to move to the other page, but it does not respond. The same thing is working when I type.
self.present (den, animated: true, completion: nil)
is working when I type.
why pushViewController not working?
Thnx
Seems self.navigationController? is nil
With storyboard:
self.navigationController?.pushViewController (trial, animated: true)
make sure the current vc is embedded inside a navigationController
without storyboard:
inside didFinishLaunchingWithOptions
self.window?.rootViewController = UINavigationController(root:ViewController())
Your current ViewController is not RootViewController.
In AppDelegate,
set this code:
var mainView = UIStoryboard(name:"Main", bundle: nil)
let viewcontroller : UIViewController = mainView.instantiateViewController(withIdentifier: "FirstScreenVC") as! FirstScreenVC
self.window!.rootViewController = viewcontroller
Now run your code,
Because now the current controller is rootViewController.
You should not instantiate a view controller like this TrialViewController ()
Instead, use:
UIStoryboard(name: "SbName", bundle: nil).instantiateViewController(withIdentifier: "VcID") as? TrialViewController
I don't use storyboard. My class is here;
import UIKit
class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
setupViews()
//register collectionview
collectionView.register(AppCell.self, forCellWithReuseIdentifier: cellId)
collectionView.delegate = self
collectionView.dataSource = self
}
let topView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.white
return view
}()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = UIColor.orange
return cv
}()
lazy var button: UIButton = {
let btn = UIButton(type: .system)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("Buton", for: .normal)
btn.backgroundColor = UIColor.orange
btn.addTarget(self, action: #selector(handleButton), for: .touchUpInside)
return btn
}()
#objc func handleButton() {
print(1212)
}
func setupViews() {
view.addSubview(topView)
topView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
topView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
topView.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
topView.heightAnchor.constraint(equalToConstant: 250).isActive = true
view.addSubview(button)
button.topAnchor.constraint(equalTo: topView.topAnchor, constant: 15).isActive = true
button.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0).isActive = true
button.widthAnchor.constraint(equalToConstant: view.frame.width - 15).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
view.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: 10).isActive = true
collectionView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
collectionView.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
collectionView.heightAnchor.constraint(equalToConstant: view.frame.height).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! AppCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height * 0.2)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let trial = Trial()
self.navigationController?.pushViewController(trial, animated: true)
}
}
class AppCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame:frame)
backgroundColor = UIColor.purple
setupViews()
}
lazy var button: UIButton = {
let btn = UIButton(type: .system)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("Buton", for: .normal)
btn.backgroundColor = UIColor.orange
btn.addTarget(self, action: #selector(handleButton), for: .touchUpInside)
return btn
}()
#objc func handleButton() {
//statusShow.showSettings()
}
func setupViews() {
addSubview(button)
button.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0).isActive = true
button.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0).isActive = true
button.widthAnchor.constraint(equalToConstant: self.frame.width * 0.3).isActive = true
button.heightAnchor.constraint(equalToConstant: self.frame.height * 0.2).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class Trial: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}

Creating UICollectionView which can scroll horizontally and vertically with Swift

I'm trying to create an UICollectionView which can scroll horizontally and vertically.
Here are my codes:
Here is the Model:
import UIKit
class AppCategory: NSObject {
var name: String?
var apps: [App]?
static func sampleAppCategories() -> [AppCategory] {
// Chapter 1
let chapter1 = AppCategory()
chapter1.name = NSLocalizedString("CHAPTER 1: ", comment: "1") + NSLocalizedString("19 Sections", comment: "title")
var apps1 = [App]()
let chapter1App = App()
chapter1App.imageName = "IMG_2487"
let chapter11App = App()
chapter11App.imageName = "IMG_2502"
let chapter12App = App()
chapter12App.imageName = "IMG_2507"
apps1.append(chapter1App)
apps1.append(chapter11App)
apps1.append(chapter12App)
chapter1.apps = apps1
// Chapter 2
let chapter2 = AppCategory()
chapter2.name = NSLocalizedString("CHAPTER 2: ", comment: "2") + NSLocalizedString("19 Sections", comment: "title")
var apps2 = [App]()
let chapter2App = App()
chapter2App.imageName = "IMG_2508"
apps2.append(chapter2App)
chapter2.apps = apps2
// Chapter 3
let chapter3 = AppCategory()
chapter3.name = NSLocalizedString("CHAPTER 3: ", comment: "title") + NSLocalizedString("19 Sections", comment: "title")
var apps3 = [App]()
let chapter3App = App()
chapter3App.imageName = "IMG_2510"
apps3.append(chapter3App)
chapter3.apps = apps3
// Chapter 4
let chapter4 = AppCategory()
chapter4.name = NSLocalizedString("CHAPTER 4: ", comment: "title") + NSLocalizedString("19 Sections", comment: "title")
var apps4 = [App]()
let chapter4App = App()
chapter4App.imageName = "IMG_2511"
apps4.append(chapter4App)
chapter4.apps = apps4
return [chapter1, chapter2, chapter3, chapter4]
}
}
class App: NSObject {
var imageName: String?
}
Here is the FeatureViewController:
import UIKit
class FeaturedViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var appCategories: [AppCategory]?
let verticalCellId = "verticalCellId"
let horizontalCellId = "horizontalCellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .white
appCategories = AppCategory.sampleAppCategories()
navigationItem.title = NSLocalizedString("Original", comment: "Original")
navigationController?.navigationBar.prefersLargeTitles = true
collectionView?.register(FeaturedVerticalCell.self, forCellWithReuseIdentifier: verticalCellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategories?.count {
return count
}
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: verticalCellId, for: indexPath) as! FeaturedVerticalCell
cell.appCategory = appCategories?[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch UIDevice.current.userInterfaceIdiom {
case .phone:
collectionView.reloadData()
return CGSize(width:view.frame.width, height: view.frame.width / 5 * 4 )
case .pad:
let padding: CGFloat = 50
let collectionViewSize = collectionView.frame.size.width - padding
collectionView.reloadData()
return CGSize(width: collectionViewSize / 5 * 4, height: collectionViewSize / 5 * 3 )
case .tv:
break
case .carPlay:
break
case .unspecified:
break
}
return CGSize(width: 0, height: 0)
}
}
Here is the FeaturedVerticalCell:
import UIKit
struct Titles {
var title: String?
var images:[String]
}
class FeaturedVerticalCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let horizontalCellId = "horizontalCellId"
var appCategory: AppCategory? {
didSet {
if let name = appCategory?.name {
titleLabel.text = name
}
}
}
let horizontalCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor.clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
if UIDevice.current.userInterfaceIdiom == .phone {
label.font = UIFont.systemFont(ofSize: 14.0, weight: UIFont.Weight.medium)
} else {
label.font = UIFont.systemFont(ofSize: 20.0, weight: UIFont.Weight.medium)
}
label.textAlignment = .left
label.textColor = UIColor.darkGray
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
horizontalCollectionView.dataSource = self
horizontalCollectionView.delegate = self
horizontalCollectionView.register(HorizontalCollectionViewCell.self, forCellWithReuseIdentifier: horizontalCellId)
addSubview(horizontalCollectionView)
horizontalCollectionView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 24).isActive = true
horizontalCollectionView.topAnchor.constraint(equalTo: self.topAnchor, constant: 36).isActive = true
horizontalCollectionView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -24).isActive = true
horizontalCollectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 8).isActive = true
addSubview(titleLabel)
titleLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 24).isActive = true
titleLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -24).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: horizontalCollectionView.topAnchor, constant: 0).isActive = true
titleLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 24).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategory?.apps?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: horizontalCellId, for: indexPath) as! HorizontalCollectionViewCell
cell.app = appCategory?.apps?[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.height * 4 / 5, height: frame.height * 4 / 5)
}
}
class HorizontalCollectionViewCell: UICollectionViewCell {
var app: App? {
didSet {
if let imageName = app?.imageName {
photoImageView.image = UIImage(named: imageName)
}
}
}
let photoImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 10
iv.image = #imageLiteral(resourceName: "IMG_2545")
iv.layer.masksToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(photoImageView)
photoImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
photoImageView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
photoImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
photoImageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
But now I got a problem: the rows in horizontal sections will change somehow when I scroll vertically. Any way to get it back to work normally?
Don't know what's wrong there on earth, but finally I got it work. Here you go:
import UIKit
class FeaturedViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
print("SeachController tapped.")
}
let firstCellId = "cellfirstCellIdId"
var appCategories: [AppCategory]?
override func viewDidLoad() {
super.viewDidLoad()
appCategories = AppCategory.sampleAppCategories()
collectionView?.backgroundColor = UIColor.clear
collectionView?.register(CategoryCell.self, forCellWithReuseIdentifier: firstCellId)
navigationItem.title = NSLocalizedString("Original", comment: "Original")
navigationController?.navigationBar.prefersLargeTitles = true
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
navigationItem.hidesSearchBarWhenScrolling = true
self.navigationItem.searchController = searchController
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: firstCellId, for: indexPath) as! CategoryCell
cell.appCategory = appCategories?[indexPath.item]
return cell
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategories?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 300)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
Here is the cell:
import UIKit
class CategoryCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var appCategory: AppCategory? {
didSet {
if let name = appCategory?.name {
firstChapterLabel.text = name
}
}
}
let secondCellId = "secondCellId"
let appsCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor.clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
let firstChapterLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
if UIDevice.current.userInterfaceIdiom == .phone {
label.font = UIFont.systemFont(ofSize: 14.0, weight: UIFont.Weight.medium)
} else {
label.font = UIFont.systemFont(ofSize: 20.0, weight: UIFont.Weight.medium)
}
label.textAlignment = .left
label.textColor = UIColor.darkGray
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(appsCollectionView)
appsCollectionView.dataSource = self
appsCollectionView.delegate = self
appsCollectionView.register(AppCell.self, forCellWithReuseIdentifier: secondCellId)
appsCollectionView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 16).isActive = true
appsCollectionView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -16).isActive = true
appsCollectionView.topAnchor.constraint(equalTo: self.topAnchor, constant: 50).isActive = true
appsCollectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
addSubview(firstChapterLabel)
firstChapterLabel.leftAnchor.constraint(equalTo: appsCollectionView.leftAnchor, constant: 16).isActive = true
firstChapterLabel.rightAnchor.constraint(equalTo: appsCollectionView.rightAnchor, constant: -16).isActive = true
firstChapterLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 24).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = appCategory?.apps?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: secondCellId, for: indexPath) as! AppCell
cell.app = appCategory?.apps?[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.height, height: frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
class AppCell: UICollectionViewCell {
var app: App? {
didSet {
if let imageName = app?.imageName {
photoImageView.image = UIImage(named: imageName)
}
}
}
let photoImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 9
iv.layer.masksToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(photoImageView)
photoImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 16).isActive = true
photoImageView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -16).isActive = true
photoImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -36).isActive = true
photoImageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 36).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And the model stays as it was described in the question.