UICollectionView Lag/frame drop - ios

Good afternoon all my app is currently dropping frames and i am not sure how to fix it...im a bit lost.
hopefully you guys/gals can point me in the right direction.
I am currently using Kingfisher to download the images from my backend, everything works well with the exception of the scrolling it's a bit choppy when you scroll down.
the controller code is as followed maybe there's something wrong with it.
i am using storyboard and have removed and added the constraints...but ive had no luck.
import UIKit
import Kingfisher
class WallpaperCVC: UICollectionViewController , UIViewControllerTransitioningDelegate {
var categoryId: String!
var categoryName: String!
var wallpaperBackend: Backend!
var result:CGSize!
// Header peralax
fileprivate let headerId = "headerId"
fileprivate let padding: CGFloat = 16
#IBOutlet var wpMain: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.title = self.categoryName // Name to the controller or section selected
// go go gadget data...
getData()
buttonGray()
setupCollectionViewLayout()
setupCollectionView()
}
func getData(){
GLOBAL_BACKEND_DATA.removeAll()
self.wallpaperBackend = Backend()
self.wallpaperBackend.getWallByIdData(id: self.categoryId) { (god) in
for post in god {
let postData = post as! NSDictionary
let wallpaperCVCId = postData.value(forKey: "id") as! String
let wallpaperCVCFile = postData.value(forKey: "file") as! String
let wallpaperCVCCategory = postData.value(forKey: "category") as! String
let wallpaperCVCDownload = Int(postData.value(forKey: "download") as! String)
GLOBAL_BACKEND_DATA.append(WallpaperModel(wallpaperModelId: wallpaperCVCId, wallpaperModelFile: wallpaperCVCFile, wallpaperModelCategoryId: wallpaperCVCCategory, wallpaperModelDownload: wallpaperCVCDownload!))
}
self.collectionView?.reloadData()
}
}
#IBAction func pressedAction(_ sender: UIButton) {
// do your stuff here
self.dismiss(animated: true, completion: nil)
print("you clicked on button \(sender.tag)")
}
func buttonGray(){
let myButton = UIButton() // if you want to set the type use like UIButton(type: .RoundedRect) or UIButton(type: .Custom)
let img = UIImage(named: "buttonA")
myButton.setImage(img, for: .normal)
// myButton.setTitleColor(UIColor.blue, for: .normal)
myButton.frame = CGRect(x: 15, y: 50, width: 100, height: 100)
// shadow
myButton.layer.shadowOpacity = 0.5
myButton.layer.shadowRadius = 4
myButton.layer.shadowOffset = CGSize(width: 0, height: 5)
myButton.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
// haptic feedback
myButton.isHaptic = true
myButton.hapticType = .impact(.medium)
//floatbutton like this
view.addSubview(myButton)
myButton.translatesAutoresizingMaskIntoConstraints = false
myButton.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 10).isActive = true
myButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// You only need one section for now
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// Here call the wallpapers
return GLOBAL_BACKEND_DATA.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// pulling & going to the controller
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "wallpapers", for: indexPath) as! WallpaperCVCell
let godData = GLOBAL_BACKEND_DATA[indexPath.row]
let imageURL = String(format:"%#uploads/image/%#", BASE_BACKEND_URL,godData.wallpaperModelFile) // calling wallpaperModel controller
let escapedURL = imageURL.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
let urls = URL(string:escapedURL!)
// if nothing load this...
let loadImage = UIImage(named: "skullLoading")
cell.collWallpaperImage.kf.setImage(with: urls, placeholder: loadImage)
cell.collWallpaperImage.kf.indicatorType = .activity // Showing a loading indicator while downloading
// lets count the downloads
let count = godData.wallpaperModelDownload as Int // calling wallpaperModel controller
cell.collectionCount.text = String(count) //collection view cell count label
cell.configure() // calling a function from the cell
return cell
}
// Lets move over to the next controller
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Tapped....Tapped go to the next controller..")
collectionView.deselectItem(at: indexPath, animated: true)
let vc = self.storyboard!.instantiateViewController(withIdentifier: "page") as! PageViewController
vc.position = indexPath.row
vc.categoryName = self.categoryName
vc.transitioningDelegate = self
vc.modalPresentationStyle = .custom
self.present(vc, animated: true, completion: nil)
}
}
// MARK: - CollectionView Delegate
extension WallpaperCVC {
// size images
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
result = CGSize(width: 187, height: 314)
if DeviceType.IS_IPHONE_5 {
result = CGSize(width: 160, height: 280)
}
if DeviceType.IS_IPHONE_6 {
result = CGSize(width: 187, height: 314)
}
if DeviceType.IS_IPHONE_6P {
result = CGSize(width: 207, height: 320)
}
if DeviceType.IS_IPHONE_4_OR_LESS {
result = CGSize(width: 151, height: 200)
}
return result;
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 15, left: 8, bottom: 5, right: 8)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
//size to all model screens to one.....NOW!!!!!!!!
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = UIScreen.main.bounds.width
return CGSize(width: (width - 28)/2, height: 350) // width & height are the same to make a square cell
}
}
// MARK: - CollectionView Layout Delegate
extension WallpaperCVC : UICollectionViewDelegateFlowLayout {
// Header peralax
fileprivate func setupCollectionViewLayout() {
let padding: CGFloat = 16
if let layout = wpMain.collectionViewLayout as? UICollectionViewFlowLayout {
layout.sectionInset = .init(top: padding, left: 0, bottom: padding, right: 0)
}
}
// Header peralax
fileprivate func setupCollectionView(){
wpMain.contentInsetAdjustmentBehavior = .never
wpMain.register(headerView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: headerId)
}
// Header peralax
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerId, for: indexPath)
return headerView
}
// Header peralax
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return .init(width: view.frame.width, height: 400)
}
}
the cell looks like this
import UIKit
import Spring
import UICountingLabel
class WallpaperCVCell: UICollectionViewCell {
override func layoutSubviews() {
super.layoutSubviews()
// self.textLabel?.frame = self.bounds
}
func configure() {
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
contentView.layer.cornerRadius = 7
contentView.layer.masksToBounds = true
// contentView.layer.borderWidth = 1.0
// contentView.layer.borderColor = UIColor.black.cgColor
}
#IBOutlet weak var collectionCount: UICountingLabel!
#IBOutlet weak var collWallpaperImage: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
}
}
&& here is the video...the initial scroll lags then it goes away but it does it everytime
iPhone app lag video
EDIT: Ok in the end i remode Kingfisher & installed SDWebImage this took care of the problem.

Based on the given code there is nothing that should cause the lag. If you could share a short video of the lag that might help understand the problem better.
And also if you could share the code that is in your cell.configure() method that would also help.
I do have a few suggestions for you that might help, you can try them on.
first of all you can move your device check
if DeviceType.IS_IPHONE_5 {
result = CGSize(width: 160, height: 280)
}
if DeviceType.IS_IPHONE_6 {
result = CGSize(width: 187, height: 314)
}
if DeviceType.IS_IPHONE_6P {
result = CGSize(width: 207, height: 320)
}
if DeviceType.IS_IPHONE_4_OR_LESS {
result = CGSize(width: 151, height: 200)
}
in viewDidLoad which will help your collection view layout to size your cells faster and not having to check this every time the user scrolls
Something like this
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
return result;
}
and
override func viewDidLoad() {
super.viewDidLoad()
self.title = self.categoryName // Name to the controller or section selected
setupDeviceSize()
// go go gadget data...
getData()
buttonGray()
setupCollectionViewLayout()
setupCollectionView()
}
func setupDeviceSize(){
if DeviceType.IS_IPHONE_5 {
result = CGSize(width: 160, height: 280)
}
if DeviceType.IS_IPHONE_6 {
result = CGSize(width: 187, height: 314)
}
if DeviceType.IS_IPHONE_6P {
result = CGSize(width: 207, height: 320)
}
if DeviceType.IS_IPHONE_4_OR_LESS {
result = CGSize(width: 151, height: 200)
}
}
Second is you can create a global variable for your place holder image
let loadImage = UIImage(named: "skullLoading")
and use that in your cellForItemAtIndexpath, which will again save loading of the placeholder image every time your cellForItemAtIndexpath is called.
You should always keep your cellForItemAtIndexpath as light as possible for a smooth scrolling experience.
Hope this helps.

Related

Custom animated transition not working when using rootview

I am trying to create a custom transition, similar to the one that is on the App Store. I used these tutorials:
https://eric-dockery283.medium.com/custom-view-transitions-like-the-new-app-store-a2a1181229b6
https://www.raywenderlich.com/2925473-ios-animation-tutorial-custom-view-controller-presentation-transitions
I have also been experimenting with using the load view to have the root view to create the view, similar to what is used in the answer used here: does loadView get called even if we don't override it in viewcontroller like the other ViewController lifecycle methods?
now what I am doing is that I am using a collectionview, which I want to keep, and when an item is pressed it animate transitions to a new view controller with a larger image, simple.
the problem is that when I combine it with using the loadView with view = rootView() it did not work in some scenarios.
For example:
when I use the modalPresentationStyle = .fullScreen, when I press an item in the collection view the screen just goes black, and then when I press the view hierarchy it shows this:
when I remove the modalPresentationStyle = .fullScreen it does animate but it is the popover transition, which I do not want.
What I want is to have a full screen transition that is animated.
here is the view controller:
class ViewController: UIViewController {
let data = createData()
var transition = PopAnimator()
var rootView = MainView()
private let collectionView: UICollectionView = {
let viewLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout)
collectionView.backgroundColor = .white
return collectionView
}()
private enum LayoutConstant {
static let spacing: CGFloat = 16.0
static let itemHeight: CGFloat = 200.0
}
override func loadView() {
view = rootView
rootView.collectionView.delegate = self
rootView.collectionView.dataSource = self
}
}
class MainView: UIView {
let collectionView: UICollectionView = {
let viewLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout)
collectionView.backgroundColor = .white
return collectionView
}()
init() {
super.init(frame: .zero)
setupLayouts()
}
private func setupLayouts() {
backgroundColor = .white
addSubview(collectionView)
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewCell.identifier)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
collectionView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor),
collectionView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as! CollectionViewCell
cell.dogImg.image = UIImage(named: data[indexPath.row].image)
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selected = data[indexPath.row]
let vc = OtherViewController()
vc.transitioningDelegate = self
// vc.modalPresentationStyle = .fullScreen
vc.data = selected
self.present(vc, animated: true)
print(selected)
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = itemWidth(for: view.frame.width, spacing: LayoutConstant.spacing)
return CGSize(width: width, height: LayoutConstant.itemHeight)
}
func itemWidth(for width: CGFloat, spacing: CGFloat) -> CGFloat {
let itemsInRow: CGFloat = 2
let totalSpacing: CGFloat = 2 * spacing + (itemsInRow - 1) * spacing
let finalWidth = (width - totalSpacing) / itemsInRow
return floor(finalWidth)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: LayoutConstant.spacing, left: LayoutConstant.spacing, bottom: LayoutConstant.spacing, right: LayoutConstant.spacing)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return LayoutConstant.spacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return LayoutConstant.spacing
}
}
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard let selectedIndexPathCell = self.collectionView.indexPathsForSelectedItems?.first,
let selectedItem = self.collectionView.cellForItem(at: selectedIndexPathCell) as? CollectionViewCell
else { return nil }
guard let originFrame = selectedItem.superview?.convert(selectedItem.frame, to: nil) else {
return transition
}
transition.originFrame = originFrame
transition.presenting = true
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.presenting = false
return transition
}
}
this is the other view that is presented to.
class OtherViewController: UIViewController, UIViewControllerTransitioningDelegate {
var data: DogArr?
var rootView = testView1()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .systemBackground
}
override func loadView() {
view = rootView
//rootView.inputViewController?.transitioningDelegate = self
rootView.carImg.image = UIImage(named: data?.image ?? "")
rootView.dismissBtn.addTarget(self, action: #selector(dismissingBtn), for: .touchUpInside)
}
#objc func dismissingBtn() {
self.dismiss(animated: true)
}
}
class testView1: UIView {
var carImg: UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFit
img.translatesAutoresizingMaskIntoConstraints = false
return img
}()
var dismissBtn: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("Done", for: .normal)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 25)
btn.titleLabel?.textAlignment = .center
btn.setTitleColor(.label, for: .normal)
btn.contentMode = .scaleAspectFill
return btn
}()
init() {
super.init(frame: .zero)
self.addSubview(carImg)
self.addSubview(dismissBtn)
carImg.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = .systemBackground
NSLayoutConstraint.activate([
carImg.topAnchor.constraint(equalTo: self.topAnchor),
carImg.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.5),
carImg.widthAnchor.constraint(equalTo: carImg.heightAnchor),
dismissBtn.topAnchor.constraint(equalTo: self.carImg.bottomAnchor, constant: 20),
dismissBtn.centerXAnchor.constraint(equalTo: self.dismissBtn.centerXAnchor),
dismissBtn.widthAnchor.constraint(equalToConstant: 150),
dismissBtn.heightAnchor.constraint(equalToConstant: 75)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I noticed that when I remove the root view and just created everything in the view controller, it seems to work, the thing is that this is just a test for now. The root view in a future project may be very large so I think keeping to a rootview may be a good idea. If there are any other methods similar, please share.
If there is anything I can answer please ask,
Thank you

Implementing search function in UICollectionView in a UICollectionViewCell

I want to implement a search function in UICollectionView in a UICollectionViewCell.
What i'm trying to achieve is a swipe-able pages with search bar.
Previously i achieved multiple pages with a search bar but not able to swipe and to match with my Android application i need to make it swipe-able.
This is the tutorial i followed on how to make it swipe-able
https://www.letsbuildthatapp.com/course_video?id=75
To make this sound less confusing, the main UICollectionView let's call it MainFrame and the MainFrame stores the two UICollectionViewCell which contains their own UICollectionView let's call it ChildFrame.
So i'm able to parse search text from MainFrame to the ChildFrame and filter the data array in the ChildFrame. Checking it on the console log, everything is working as intended. The filtered result is correct. But my ChildFrame is not updating to the latest data array. Here's the code below.
Here's the code for ChildFrame
class ChildFrameCell1: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
var dataArray = [CustomObject]()
var filteredData = [CustomObject]()
var isFiltering: Bool = false
var mainFrame: MainFrame?
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 4, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0.0
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .white
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
populateCollectionView()
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: cellId)
addSubview(collectionView)
collectionView.anchor(top: self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func searchTypeAndText(type: String, text: String, filtering: Bool) {
isFiltering = filtering
filteredData = dataArray.filter({( object : CustomObject) -> Bool in
if type == "type" {
return object.type.lowercased().contains(text.lowercased())
} else {
return object.type.lowercased().contains(text.lowercased())
}
})
DispatchQueue.main.async {
self.collectionView.reloadData()
self.collectionView.layoutSubviews()
}
}
func populateCollectionView() {
// load data into dataArray
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if isFiltering {
// during searching this will be triggered and return the correct count for the filteredData
return filteredData.count
} else {
return dataArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell
var object: CustomObject
// isFiltering is always false even when searching
if isFiltering {
// never called
object = filteredData[indexPath.row]
} else {
// always called even when searching
object = dataArray[indexPath.row]
}
// do cell things here
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width - 8, height: 120)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
cell.contentView.layer.masksToBounds = true
let radius = cell.contentView.layer.cornerRadius
cell.layer.shadowPath = UIBezierPath(roundedRect: cell.bounds, cornerRadius: radius).cgPath
}
}
Here's the code for MainFrame
class MainFrame: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let searchController = UISearchController(searchResultsController: nil)
let cellId = "cellId"
let childFrameCell1Id = "cell1Id"
let childFrameCell2Id = "cell2Id"
var childFrameCell1: ChildFrameCell1!
var childFrameCell2: ChildFrameCell2!
lazy var topBar: CustomTopBar = {
let tb = CustomTopBar()
tb.mainFrame = self
return tb
}() // Top bar sliding indicator
override func viewDidLoad() {
super.viewDidLoad()
configureView()
configureSearchBar()
configureCollectionView()
setupTopBar()
}
func configureCollectionView() {
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
flowLayout.minimumInteritemSpacing = 0
}
collectionView.backgroundColor = .white
collectionView.register(ChildFrameCell1.self, forCellWithReuseIdentifier: childFrameCell1Id)
collectionView.register(ChildFrameCell2.self, forCellWithReuseIdentifier: childFrameCell2Id)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.isPagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
collectionView.collectionViewLayout.invalidateLayout()
collectionView.contentInset = UIEdgeInsets(top: 45, left: 0, bottom: 0, right: 0)
collectionView.scrollIndicatorInsets = UIEdgeInsets(top: 45, left: 0, bottom: 0, right: 0)
collectionView.contentInsetAdjustmentBehavior = UIScrollView.ContentInsetAdjustmentBehavior.never
childFrameCell1 = ChildFrameCell1()
childFrameCell2 = ChildFrameCell2()
}
func setupTopBar() {
// add top bar
}
func configureView() {
// set up view
}
func configureSearchBar() {
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search"
searchController.searchBar.tintColor = .white
searchController.searchBar.barTintColor = .white
if let tf = searchController.searchBar.value(forKey: "searchField") as? UITextField {
tf.textColor = UIColor.white
if let backgroundView = tf.subviews.first {
backgroundView.backgroundColor = .white
backgroundView.layer.cornerRadius = 10
backgroundView.clipsToBounds = true
}
}
navigationItem.searchController = searchController
definesPresentationContext = true
searchController.searchBar.scopeButtonTitles = ["Type1", "Type2"]
searchController.searchBar.delegate = self
}
func searchBarIsEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func isFiltering() -> Bool {
let searchBarScopeIsFiltering = searchController.searchBar.selectedScopeButtonIndex != 0
return searchController.isActive && (!searchBarIsEmpty() || searchBarScopeIsFiltering)
}
func filterContentForSearchText(_ searchText: String, scope: String = "Location") {
let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
let visibleIndexPath = collectionView.indexPathForItem(at: visiblePoint)
if visibleIndexPath?.row == 0 {
// find the current view and parse search text into childFrame
childFrameCell1.searchTypeAndText(type: scope, text: searchText, filtering: isFiltering())
} else {
childFrameCell2.searchTypeAndText(type: scope, text: searchText, filtering: isFiltering())
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2 // 2 pages
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let id: String
currentView = indexPath.item
if currentView == 0 {
id = childFrameCell1Id
} else {
id = childFrameCell2Id
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: id, for: indexPath) as! ChildFrameCell1
cell.mainFrame = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height - 50)
}
}
extension MainFrame: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}
}
extension MainFrame: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
ChildFrameCell2 is a subclass of ChildFrameCell1
When i enter text in the search bar in the MainFrame, it doesn't reload the collectionView to the new filteredData even if filteredData contains data.
I've been trying many ways to do this but still unable to reload the collectionView.
I even tried using DispatchGroup but the result is still the same. Filtering is not an issue, everything works accordingly except for reloading the collectionView.
Thank you for reading this lengthy post.
If you need the code from the MainFrame do let me know.

CollectionView in the storyboard is overwriting the cell size

I have a collection view that has cells that change size depending on what size the user has selected: Small, medium, large. I have the protocol UICollectionViewDelegateFlowLayout and the delegate function:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
The function correctly sets the size of the area needed for the cell but doesn't resize the cell itself.
here is what my view looks like when the cells have been set to the small setting:
Here is the view with the default size:
As you can see the space for the cell changes but the cell size doesn't change.
In the storyboard, whatever the cell size is set to, is what the size will be regardless of what I do in the code. Does anyone know how I can override this:
Any help would be greatly appreciated. Thanks
EDIT
The delegate function is being called. the size is changed on a different page. The size is stored as a value in a singleton, the delegate function decides what size to use based on that value. Here is the delegate function:
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
// if self.collectionView == collectionView {
let viewHeight = collectionView.frame.height
let smallSize = (viewHeight / 4) - 20
let mediumSize = (viewHeight / 2) - 20
let largeSize = viewHeight - 20
switch gridSquareSize {
case .small:
return CGSize(width: smallSize, height: smallSize)
case .standard:
return CGSize(width: mediumSize, height: mediumSize)
case .large:
return CGSize(width: largeSize, height: largeSize)
}
//}
}
EDIT 2:
Here is the function that sets up the cell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "galleryCell", for: indexPath)
as! GalleryCell
cell.layoutIfNeeded()
switch SettingsModel.instance.currentGridSquareSize {
case .small:
cell.galleryName.font = cell.galleryName.font.withSize(12)
break
case .standard:
cell.galleryName.font = cell.galleryName.font.withSize(24)
break
case .large:
cell.galleryName.font = cell.galleryName.font.withSize(36)
break
}
cell.isHidden = false
cell.background.backgroundColor = UIColor.white
cell.plusIcon.isHidden = true
cell.topLeftImage.isHidden = false
cell.galleryName.textColor = UIColor(red: 45, green: 64, blue: 83, alpha: 1)
cell.topLeftImage.image = UIImage(named: "PlaceholderImage")
cell.delete.isHidden = !isInEditMode
let position = indexPath.row
if position == mGalleriesList.count {
if isInEditMode {
cell.background.backgroundColor = UIColor.darkGray
cell.galleryName.text = "Add new gallery"
cell.galleryName.textColor = UIColor.white
cell.plusIcon.isHidden = false
cell.topLeftImage.isHidden = true
cell.delete.isHidden = true
}
else {
cell.isHidden = true
}
}
else {
let gallery = mGalleriesList[position]
cell.data = gallery
cell.plusIcon.isHidden = true
if let galleryName = gallery.galleryName {
cell.galleryName.text = galleryName
}
var mediaEntries = [MediaEntry]()
for entry in gallery.mediaEntries {
if let deleted = entry.deleted {
if !deleted {
mediaEntries.append(entry)
}
}
else {
mediaEntries.append(entry)
}
}
if mediaEntries.count > 0 {
if let downloadUrl = mediaEntries[0].downloadUrl {
setImage(storageUrl: downloadUrl, imageView: cell.topLeftImage, placeholder: placeholder!, cropToBounds: false)
}
}
}
return cell
}
Here is also a screenshot of the cell inside the storyboard:
Hope this additional information helps

CollectionView Auto Scroll

So I have some code that I am using to auto scroll a collectionView and it is giving me lots of problems. It works based off of a timer that calls a certain function every 3.5 seconds to scroll to a certain item in the collectionView. The code is included belwo
import UIKit
class HomeFeedCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UIScrollViewDelegate {
var homeFeedController: HomeFeedController?
let emptyView = UIView()
//Timer user for call autoscroller of top collection view
private var timer:Timer?
var scrollTimer = Timer()
private let cellId = "cellId"
var featuredEvents: [Event]?{
didSet {
homeFeedCollectionView.reloadData()
}
}
var titles: String? {
didSet {
guard let titles = titles else {
return
}
// let attributedText = NSMutableAttributedString(string: titles, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 100)])
sectionNameLabel.text = titles
// sectionNameLabel.attributedText = attributedText
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setTimer()
setupViews()
}
func setTimer(){
//auto scroll method to call every 2.5 seconds interval
self.timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.startTimer(theTimer:)), userInfo: nil, repeats: true)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let sectionNameLabel : UILabel = {
let sectionNameLabel = UILabel()
sectionNameLabel.font = UIFont(name:"HelveticaNeue-CondensedBlack", size: 36.0)
return sectionNameLabel
}()
lazy var emptyLabel: UILabel = {
let emptyLabel = UILabel()
emptyLabel.text = "Sorry We Currently Have No Events, \n In This Category Near You"
emptyLabel.font = UIFont(name: "Avenir", size: 14)
emptyLabel.numberOfLines = 0
emptyLabel.textAlignment = .center
return emptyLabel
}()
lazy var iconImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
}()
let homeFeedCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .clear
return cv
}()
#objc func setupViews(){
backgroundColor = .clear
addSubview(homeFeedCollectionView)
addSubview(sectionNameLabel)
sectionNameLabel.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 2, paddingLeft: 4, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
homeFeedCollectionView.anchor(top: sectionNameLabel.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
homeFeedCollectionView.delegate = self
homeFeedCollectionView.dataSource = self
homeFeedCollectionView.showsHorizontalScrollIndicator = false
homeFeedCollectionView.register(HomeFeedEventCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let currentEventCount = featuredEvents?.count else{
return 0
}
if currentEventCount == 0 {
print("no events")
setupEmptyDataSet()
}else{
emptyView.removeFromSuperview()
}
return currentEventCount
}
#objc func setupEmptyDataSet(){
self.addSubview(emptyView)
emptyView.backgroundColor = .clear
emptyView.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
emptyView.addSubview(iconImageView)
iconImageView.image = UIImage(named: "icons8-face-100")
iconImageView.snp.makeConstraints { (make) in
make.center.equalTo(emptyView)
}
emptyView.addSubview(emptyLabel)
emptyLabel.snp.makeConstraints { (make) in
make.bottom.equalTo(iconImageView.snp.bottom).offset(30)
make.left.right.equalTo(emptyView)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 20, left: 5, bottom: 20, right: 5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width - 40, height: frame.height - 40)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let eventDetails = EventDetailViewController()
eventDetails.currentEvent = featuredEvents?[indexPath.item]
homeFeedController?.navigationController?.pushViewController(eventDetails, animated: true)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! HomeFeedEventCell
cell.event = featuredEvents?[indexPath.item]
var rowIndex = indexPath.row
let numberOfRecords = (featuredEvents?.count)! - 1
if rowIndex < numberOfRecords {
rowIndex = rowIndex + 1
}else{
rowIndex = 0
}
scrollTimer = Timer.scheduledTimer(timeInterval: 3.5, target: self, selector: #selector(self.startTimer(theTimer:)), userInfo: rowIndex, repeats: true)
return cell
}
#objc func startTimer(theTimer: Timer){
UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseOut, animations: {
if let currentIndexPath = self.homeFeedCollectionView.indexPathsForVisibleItems.last{
//Check visible cell is last cell of top collection view then set first index as visible
if currentIndexPath.item == self.homeFeedCollectionView.numberOfItems(inSection: 0)-1{
let nextIndexPath = NSIndexPath(item: 0, section: 0)
//top collection view scroller in first item
self.homeFeedCollectionView.scrollToItem(at: nextIndexPath as IndexPath, at: .right, animated: false)
}else{
//create next index path from current index path of the top collection view
let nextIndexPath = NSIndexPath(item: currentIndexPath.item + 1, section: 0)
//top collection view scroller to next item
self.homeFeedCollectionView.scrollToItem(at: nextIndexPath as IndexPath, at: .left, animated: true)
}
}
}) { (finished) in
if finished {
print("done")
}
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.homeFeedCollectionView.scrollToNearestVisibleCollectionViewCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
self.homeFeedCollectionView.scrollToNearestVisibleCollectionViewCell()
}
}
}
Im not really sure what I am doing wrong, to be honest. But my problems with this approach are
When you I first enter the collectionView it goes straight to the third item
It sometimes scrolls to a certain item may be the first or the third and then it just gets stuck there and stops moving. It tried to move but it just keeps getting stuck.
You have a viewController for the view that shows up what you want and contains collectionView. This class should implement UICollectionViewDataSource and UICollectionViewDelegate:
class viewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource
in this class you should implement the numberOfItemsInSection and cellForItemAt methods.
and you need another class for cells in the collectionView:
class customCell: UICollectionViewCell
in this class you could define IBOutlets or what ever you want related to each cell NOT collectionView
and in last you should fire the timer in viewDidLoad (or viewDidAppear) instead of cellForItemAt delegate,
that func calls for each row then you start timers as number of cells that your collectionView has.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
scrollTimer = Timer.scheduledTimer(timeInterval: 3.5, target: self, selector: #selector(self.startTimer(theTimer:)), userInfo: rowIndex, repeats: true)
}
You can use this pod https://cocoapods.org/pods/AutoScrollCollectionView
https://github.com/mohankrishnameruva/AutoScrollCollectionView
Subclass your collection view from 'AutoScrollCollectionView' in storyboard and
whenever you want to start scroll call this method
on collectionView startAutoScrolling(withTimeInterval: TimeInterval(exactly: 2.0)!)

Issue With Passing Data Programmatically Swift

I am having trouble passing data from one UICollectionViewController to another UICollectionViewController programmatically.
Currently my setup is as follows:
The UICollectionViewController that is passing the data (RestaurantController)
1a. A UICollectionViewCell (RestaurantCell)
this UICollectionViewCell has a nested UICollectionViewController within it with another custom UICollectionViewCell (RestaurantCollectionViewCell)
The UICollectionViewController that is receiving the data (MenuController)
2a. A UICollectionViewCell (MenuCell)
Inside of my RestaurantCell I am loading data from JSON and appending it a new array as called restaurants: var restaurants = [RestaurantModel](). But when I try to load the restaurant name or any of the restaurant objects in my MenuController using var restaurant: RestaurantModel?, I get nil values. I have a feeling either my setup is incorrect or I'm making a stupid mistake somewhere. Perhaps both. I have pasted my code below for each class.
Where the values are returning nil inside of MenuController:
print("Restaurant Name:", restaurant?.name)
print("Restaurant Id:", restaurant?.id)
Is the custom delegation causing the issue?
You help and advice is greatly appreciated!
Inside of my RestaurantController:
import UIKit
import FBSDKLoginKit
class RestaurantController: UICollectionViewController, UICollectionViewDelegateFlowLayout, SWRevealViewControllerDelegate, UISearchBarDelegate, RestaurantDelegate {
var restaurantCell: RestaurantCell?
private let restaurantCellId = "restaurantCellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.qpizzaWhite()
collectionView?.register(RestaurantCell.self, forCellWithReuseIdentifier: restaurantCellId)
if self.revealViewController() != nil {
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "icon_menu_24dp").withRenderingMode(.alwaysOriginal), style: .plain, target: self.revealViewController(), action: #selector(SWRevealViewController.revealToggle(_:)))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
}
// FIXME: issue with this...navigationcontroller is presenting, not pushing ontop of stack view
func didTapRestaurantCell(cell: RestaurantCell) {
print("Did Tap Restaurant Cell - Restaurant Controller")
let layout = UICollectionViewFlowLayout()
let controller = MenuController(collectionViewLayout: layout)
navigationController?.pushViewController(controller, animated: true)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: restaurantCellId, for: indexPath) as! RestaurantCell
cell.delegate = self
return cell
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
}
Inside of my RestaurantCell:
protocol RestaurantDelegate {
func didTapRestaurantCell(cell: RestaurantCell)
}
class RestaurantCell: BaseCell, UISearchBarDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var delegate: RestaurantDelegate?
var restaurants = [RestaurantModel]()
var filteredRestaurants = [RestaurantModel]()
private let restaurantCollectionViewCell = "restaurantCollectionViewCell"
private let activityIndicator = UIActivityIndicatorView()
lazy var searchBar: UISearchBar = {
let sb = UISearchBar()
sb.placeholder = "Search Restaurant"
sb.barTintColor = .white
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).backgroundColor = UIColor.qpizzaWhite()
sb.delegate = self
return sb
}()
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .white
return cv
}()
override func setupViews() {
super.setupViews()
collectionView.register(RestaurantCollectionViewCell.self, forCellWithReuseIdentifier: restaurantCollectionViewCell)
collectionView.delegate = self
collectionView.dataSource = self
backgroundColor = UIColor.qpizzaRed()
addSubview(searchBar)
addSubview(collectionView)
_ = searchBar.anchor(topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, topConstant: 4, leftConstant: 4, bottomConstant: 0, rightConstant: 4, widthConstant: 0, heightConstant: 50)
_ = collectionView.anchor(searchBar.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, topConstant: 0, leftConstant: 0, bottomConstant: 0, rightConstant: 0, widthConstant: 0, heightConstant: 0)
loadRestaurants()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
filteredRestaurants = self.restaurants.filter({ (restaruant: RestaurantModel) -> Bool in
return restaruant.name?.lowercased().range(of: searchText.lowercased()) != nil
})
self.collectionView.reloadData()
}
// MARK - Helper Methods
func loadRestaurants() {
showActivityIndicator()
APIManager.shared.getRestaurants { (json) in
if json != .null {
// print("Restaurant JSON:", json)
self.restaurants = []
if let restaurantList = json["restaurants"].array {
for item in restaurantList {
let restaurant = RestaurantModel(json: item)
self.restaurants.append(restaurant)
}
self.collectionView.reloadData()
self.hideActivityIndicator()
}
} else {
print("Error loading JSON into Restaurant ViewController")
}
}
}
func loadImage(imageView: UIImageView, urlString: String) {
let imageUrl: URL = URL(string: urlString)!
URLSession.shared.dataTask(with: imageUrl) { (data, response, error) in
if let error = error {
print("Error loading image for Restaurant Controller:", error.localizedDescription)
}
guard let data = data, error == nil else { return }
DispatchQueue.main.async(execute: {
imageView.image = UIImage(data: data)
})
}.resume()
}
func showActivityIndicator() {
activityIndicator.frame = CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0)
activityIndicator.center = center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
activityIndicator.color = UIColor.qpizzaGold()
addSubview(activityIndicator)
activityIndicator.startAnimating()
}
func hideActivityIndicator() {
activityIndicator.stopAnimating()
}
//MARK: CollectionView Delegate & DataSource Methods
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: restaurantCollectionViewCell, for: indexPath) as! RestaurantCollectionViewCell
let restaurant: RestaurantModel
if searchBar.text != "" {
restaurant = filteredRestaurants[indexPath.item]
} else {
restaurant = restaurants[indexPath.item]
}
cell.restaurantNameLabel.text = restaurant.name
cell.restaurantAddressLabel.text = restaurant.address
if let logoName = restaurant.logo {
let url = "\(logoName)"
loadImage(imageView: cell.restaurantLogoImageView, urlString: url)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if searchBar.text != "" {
return self.filteredRestaurants.count
}
return self.restaurants.count
}
//FIXME: Restaurant Name Navigation Title is still not be passed from RestaurantCell to MenuController
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Did Select Item - Restaurant Cell")
let layout = UICollectionViewFlowLayout()
let controller = MenuController(collectionViewLayout: layout)
controller.restaurant = self.restaurants[indexPath.item]
print("Controller", controller.restaurant) // Optional(QpizzaDelivery.RestaurantModel)
print("Restaurant:", self.restaurants) // [QpizzaDelivery.RestaurantModel, QpizzaDelivery.RestaurantModel, QpizzaDelivery.RestaurantModel]
print("IndexPath:", self.restaurants[indexPath.item]) // QpizzaDelivery.RestaurantModel
delegate?.didTapRestaurantCell(cell: self)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: frame.width, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0.5
}
}
Inside of my MenuController:
import UIKit
class MenuController: UICollectionViewController, UICollectionViewDelegateFlowLayout, SWRevealViewControllerDelegate {
private let menuCellId = "menuCellId"
var restaurant: RestaurantModel?
var menuItems = [MenuItemsModel]()
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.qpizzaWhite()
collectionView?.register(MenuCell.self, forCellWithReuseIdentifier: menuCellId)
collectionView?.alwaysBounceVertical = true
if self.revealViewController() != nil {
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "menu2-black-32").withRenderingMode(.alwaysOriginal), style: .plain, target: self.revealViewController(), action: #selector(SWRevealViewController.revealToggle(_:)))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
print("Restaurant Name:", restaurant?.name) // returns nil
if let restaurantName = restaurant?.name {
self.navigationItem.title = restaurantName
}
loadMenuItems()
}
func loadMenuItems() {
print("Restaurant Id:", restaurant?.id) // returns nil
if let restaurantId = restaurant?.id {
print("RestaurantId:", restaurantId)
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: menuCellId, for: indexPath) as! MenuCell
return cell
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let layout = UICollectionViewFlowLayout()
let controller = MenuDetailsController(collectionViewLayout: layout)
navigationController?.pushViewController(controller, animated: true)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 120)
}
}
Inside of my MenuCell:
import UIKit
class MenuCell: BaseCell {
let restaurantLabel: UILabel = {
let label = UILabel()
label.text = "Restaurant King"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = .black
label.numberOfLines = 0
return label
}()
let mealImageView: UIImageView = {
let iv = UIImageView()
iv.image = #imageLiteral(resourceName: "button_chicken").withRenderingMode(.alwaysOriginal)
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
return iv
}()
let mealDetailsLabel: UILabel = {
let label = UILabel()
label.text = "Grass fed grass, American cheese, and friez"
label.font = UIFont.boldSystemFont(ofSize: 12)
label.textColor = UIColor.qpizzaBlack()
label.numberOfLines = 0
return label
}()
let mealPriceLabel: UILabel = {
let label = UILabel()
label.text = "$12.00"
label.font = UIFont.boldSystemFont(ofSize: 12)
label.textColor = UIColor.qpizzaBlack()
return label
}()
let sepereatorView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.lightGray
return view
}()
override func setupViews() {
super.setupViews()
backgroundColor = UIColor.qpizzaWhite()
addSubview(restaurantLabel)
addSubview(mealImageView)
addSubview(mealDetailsLabel)
addSubview(mealPriceLabel)
addSubview(sepereatorView)
_ = mealImageView.anchor(topAnchor, left: nil, bottom: nil, right: rightAnchor, topConstant: 14, leftConstant: 0, bottomConstant: 0, rightConstant: 12, widthConstant: 60, heightConstant: 60)
_ = restaurantLabel.anchor(topAnchor, left: leftAnchor, bottom: nil, right: mealImageView.leftAnchor, topConstant: 14, leftConstant: 12, bottomConstant: 0, rightConstant: 10, widthConstant: 0, heightConstant: 20)
_ = mealDetailsLabel.anchor(restaurantLabel.bottomAnchor, left: leftAnchor, bottom: nil, right: mealImageView.leftAnchor, topConstant: 12, leftConstant: 12, bottomConstant: 0, rightConstant: 10, widthConstant: 0, heightConstant: 30)
_ = mealPriceLabel.anchor(mealDetailsLabel.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, topConstant: 10, leftConstant: 12, bottomConstant: 10, rightConstant: 10, widthConstant: 0, heightConstant: 20)
_ = sepereatorView.anchor(nil, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, topConstant: 0, leftConstant: 20, bottomConstant: 4, rightConstant: 20, widthConstant: 0, heightConstant: 1)
}
}
Just a quick look, declaring a variable of the correct type is the start. But you actually have to do an assignment (=) to move data or the class reference from once class to the next.
func didTapRestaurantCell(cell: RestaurantCell) {
print("Did Tap Restaurant Cell - Restaurant Controller")
let layout = UICollectionViewFlowLayout()
let controller = MenuController(collectionViewLayout: layout)
navigationController?.pushViewController(controller, animated: true)
// you need to set the restaurant attribute of your new
// controller
let indexPath = indexPath(for: cell)
controller.restaurant = self.restaurants[indexPath.item]
}
Thank you to everyone who commented and assisted me in figuring this out. I was able to solve my issue by changing my protocol and passing the controller as a parameter.
In RestaurantCell:
protocol RestaurantDelegate {
func didTapRestaurantCell(cell: RestaurantCell, withMenuController: MenuController)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Did Select Item - Restaurant Cell")
let layout = UICollectionViewFlowLayout()
let controller = MenuController(collectionViewLayout: layout)
controller.restaurant = self.restaurants[indexPath.item]
delegate?.didTapRestaurantCell(cell: self, withMenuController: controller)
}
In RestaurantController:
func didTapRestaurantCell(cell: RestaurantCell, withMenuController controller: MenuController) {
print("Did Tap Restaurant Cell - Restaurant Controller")
navigationController?.pushViewController(controller, animated: true)
}

Resources