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

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

your awakeFromNib method is probably firing before your set your cell's translation property. Try setting your label text in your translation's setter:
set {
_translation = newValue
wordLabel?.text = _translation?.word
}

Related

How to track a CollectionView cell by time in Swift

I've been working on a feature to detect when a user sees a post and when he doesn't. When the user does see the post I turn the cell's background into green, when it doesn't then it stays red. Now after doing that I notice that I turn on all the cells into green even tho the user only scroll-down the page, so I added a timer but I couldn't understand how to use it right so I thought myself maybe you guys have a suggestion to me cause I'm kinda stuck with it for like two days :(
Edit: Forgot to mention that a cell marks as seen if it passes the minimum length which is 2 seconds.
Here's my Code:
My VC(CollectionView):
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate {
var impressionEventStalker: ImpressionStalker?
var impressionTracker: ImpressionTracker?
var indexPathsOfCellsTurnedGreen = [IndexPath]() // All the read "posts"
#IBOutlet weak var collectionView: UICollectionView!{
didSet{
collectionView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
impressionEventStalker = ImpressionStalker(minimumPercentageOfCell: 0.70, collectionView: collectionView, delegate: self)
}
}
func registerCollectionViewCells(){
let cellNib = UINib(nibName: CustomCollectionViewCell.nibName, bundle: nil)
collectionView.register(cellNib, forCellWithReuseIdentifier: CustomCollectionViewCell.reuseIdentifier)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionView.delegate = self
collectionView.dataSource = self
registerCollectionViewCells()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
impressionEventStalker?.stalkCells()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
impressionEventStalker?.stalkCells()
}
}
// MARK: CollectionView Delegate + DataSource Methods
extension ViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.reuseIdentifier, for: indexPath) as? CustomCollectionViewCell else {
fatalError()
}
customCell.textLabel.text = "\(indexPath.row)"
if indexPathsOfCellsTurnedGreen.contains(indexPath){
customCell.cellBackground.backgroundColor = .green
}else{
customCell.cellBackground.backgroundColor = .red
}
return customCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 225)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) // Setting up the padding
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//Start The Clock:
if let trackableCell = cell as? TrackableView {
trackableCell.tracker = ImpressionTracker(delegate: trackableCell)
trackableCell.tracker?.start()
}
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//Stop The Clock:
(cell as? TrackableView)?.tracker?.stop()
}
}
// MARK: - Delegate Method:
extension ViewController:ImpressionStalkerDelegate{
func sendEventForCell(atIndexPath indexPath: IndexPath) {
guard let customCell = collectionView.cellForItem(at: indexPath) as? CustomCollectionViewCell else {
return
}
customCell.cellBackground.backgroundColor = .green
indexPathsOfCellsTurnedGreen.append(indexPath) // We append all the visable Cells into an array
}
}
my ImpressionStalker:
import Foundation
import UIKit
protocol ImpressionStalkerDelegate:NSObjectProtocol {
func sendEventForCell(atIndexPath indexPath:IndexPath)
}
protocol ImpressionItem {
func getUniqueId()->String
}
class ImpressionStalker: NSObject {
//MARK: Variables & Constants
let minimumPercentageOfCell: CGFloat
weak var collectionView: UICollectionView?
static var alreadySentIdentifiers = [String]()
weak var delegate: ImpressionStalkerDelegate?
//MARK: Initializer
init(minimumPercentageOfCell: CGFloat, collectionView: UICollectionView, delegate:ImpressionStalkerDelegate ) {
self.minimumPercentageOfCell = minimumPercentageOfCell
self.collectionView = collectionView
self.delegate = delegate
}
// Checks which cell is visible:
func stalkCells() {
for cell in collectionView!.visibleCells {
if let visibleCell = cell as? UICollectionViewCell & ImpressionItem {
let visiblePercentOfCell = percentOfVisiblePart(ofCell: visibleCell, inCollectionView: collectionView!)
if visiblePercentOfCell >= minimumPercentageOfCell,!ImpressionStalker.alreadySentIdentifiers.contains(visibleCell.getUniqueId()){ // >0.70 and not seen yet then...
guard let indexPath = collectionView!.indexPath(for: visibleCell), let delegate = delegate else {
continue
}
delegate.sendEventForCell(atIndexPath: indexPath) // send the cell's index since its visible.
ImpressionStalker.alreadySentIdentifiers.append(visibleCell.getUniqueId()) // to avoid double events to show up.
}
}
}
}
// Func Which Calculate the % Of Visible of each Cell:
private func percentOfVisiblePart(ofCell cell:UICollectionViewCell, inCollectionView collectionView:UICollectionView) -> CGFloat{
guard let indexPathForCell = collectionView.indexPath(for: cell),
let layoutAttributes = collectionView.layoutAttributesForItem(at: indexPathForCell) else {
return CGFloat.leastNonzeroMagnitude
}
let cellFrameInSuper = collectionView.convert(layoutAttributes.frame, to: collectionView.superview)
let interSectionRect = cellFrameInSuper.intersection(collectionView.frame)
let percentOfIntersection: CGFloat = interSectionRect.height/cellFrameInSuper.height
return percentOfIntersection
}
}
ImpressionTracker:
import Foundation
import UIKit
protocol ViewTracker {
init(delegate: TrackableView)
func start()
func pause()
func stop()
}
final class ImpressionTracker: ViewTracker {
private weak var viewToTrack: TrackableView?
private var timer: CADisplayLink?
private var startedTimeStamp: CFTimeInterval = 0
private var endTimeStamp: CFTimeInterval = 0
init(delegate: TrackableView) {
viewToTrack = delegate
setupTimer()
}
func setupTimer() {
timer = (viewToTrack as? UIView)?.window?.screen.displayLink(withTarget: self, selector: #selector(update))
timer?.add(to: RunLoop.main, forMode: .default)
timer?.isPaused = true
}
func start() {
guard viewToTrack != nil else { return }
timer?.isPaused = false
startedTimeStamp = CACurrentMediaTime() // Current Time in seconds.
}
func pause() {
guard viewToTrack != nil else { return }
timer?.isPaused = true
endTimeStamp = CACurrentMediaTime()
print("Im paused!")
}
func stop() {
timer?.isPaused = true
timer?.invalidate()
}
#objc func update() {
guard let viewToTrack = viewToTrack else {
stop()
return
}
guard viewToTrack.precondition() else {
startedTimeStamp = 0
endTimeStamp = 0
return
}
endTimeStamp = CACurrentMediaTime()
trackIfThresholdCrossed()
}
private func trackIfThresholdCrossed() {
guard let viewToTrack = viewToTrack else { return }
let elapsedTime = endTimeStamp - startedTimeStamp
if elapsedTime >= viewToTrack.thresholdTimeInSeconds() {
viewToTrack.viewDidStayOnViewPortForARound()
startedTimeStamp = endTimeStamp
}
}
}
my customCell:
import UIKit
protocol TrackableView: NSObject {
var tracker: ViewTracker? { get set }
func thresholdTimeInSeconds() -> Double //Takes care of the screen's time, how much "second" counts.
func viewDidStayOnViewPortForARound() // Counter for how long the "Post" stays on screen.
func precondition() -> Bool // Checks if the View is full displayed so the counter can go on fire.
}
class CustomCollectionViewCell: UICollectionViewCell {
var tracker: ViewTracker?
static let nibName = "CustomCollectionViewCell"
static let reuseIdentifier = "customCell"
#IBOutlet weak var cellBackground: UIView!
#IBOutlet weak var textLabel: UILabel!
var numberOfTimesTracked : Int = 0 {
didSet {
self.textLabel.text = "\(numberOfTimesTracked)"
}
}
override func awakeFromNib() {
super.awakeFromNib()
cellBackground.backgroundColor = .red
layer.borderWidth = 0.5
layer.borderColor = UIColor.lightGray.cgColor
}
override func prepareForReuse() {
super.prepareForReuse()
print("Hello")
tracker?.stop()
tracker = nil
}
}
extension CustomCollectionViewCell: ImpressionItem{
func getUniqueId() -> String {
return self.textLabel.text!
}
}
extension CustomCollectionViewCell: TrackableView {
func thresholdTimeInSeconds() -> Double { // every 2 seconds counts as a view.
return 2
}
func viewDidStayOnViewPortForARound() {
numberOfTimesTracked += 1 // counts for how long the view stays on screen.
}
func precondition() -> Bool {
let screenRect = UIScreen.main.bounds
let viewRect = convert(bounds, to: nil)
let intersection = screenRect.intersection(viewRect)
return intersection.height == bounds.height && intersection.width == bounds.width
}
}
The approach you probably want to use...
In you posted code, you've created an array of "read posts":
var indexPathsOfCellsTurnedGreen = [IndexPath]() // All the read "posts"
Assuming your real data will have multiple properties, such as:
struct TrackPost {
var message: String = ""
var postAuthor: String = ""
var postDate: Date = Date()
// ... other stuff
}
add another property to track whether or not it has been "seen":
struct TrackPost {
var message: String = ""
var postAuthor: String = ""
var postDate: Date = Date()
// ... other stuff
var hasBeenSeen: Bool = false
}
Move all of your "tracking" code out of the controller, and instead add a Timer to your cell class.
When the cell appears:
if hasBeenSeen for that cell's Data is false
start a 2-second timer
if the timer elapses, the cell has been visible for 2 seconds, so set hasBeenSeen to true (use a closure or protocol / delegate pattern to tell the controller to update the data source) and change the backgroundColor
if the cell is scrolled off-screen before the timer elapses, stop the timer and don't tell the controller anything
if hasBeenSeen is true to begin with, don't start the 2-second timer
Now, your cellForItemAt code will look something like this:
let p: TrackPost = myData[indexPath.row]
customCell.authorLabel.text = p.postAuthor
customCell.dateLabel.text = myDateFormat(p.postDate) // formatted as a string
customCell.textLabel.text = p.message
// setting hasBeenSeen in your cell should also set the backgroundColor
// and will be used so the cell knows whether or not to start the timer
customCell.hasBeenSeen = p.hasBeenSeen
// this will be called by the cell if the timer elapsed
customCell.wasSeenCallback = { [weak self] in
guard let self = self else { return }
self.myData[indexPath.item].hasBeenSeen = true
}
What about a much simpler approach like:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
for subview in collectionView!.visibleCells {
if /* check visible percentage */ {
if !(subview as! TrackableCollectionViewCell).timerRunning {
(subview as! TrackableCollectionViewCell).startTimer()
}
} else {
if (subview as! TrackableCollectionViewCell).timerRunning {
(subview as! TrackableCollectionViewCell).stopTimer()
}
}
}
}
With a Cell-Class extended by:
class TrackableCollectionViewCell {
static let minimumVisibleTime: TimeInterval = 2.0
var timerRunning: Bool = true
private var timer: Timer = Timer()
func startTimer() {
if timerRunning {
return
}
timerRunning = true
timer = Timer.scheduledTimer(withTimeInterval: minimumVisibleTime, repeats: false) { (_) in
// mark cell as seen
}
}
func stopTimer() {
timerRunning = false
timer.invalidate()
}
}

How can I execute the collectionView methods of a class from another one?

I have my class CardSensors which is has a collectionView which is filled with another XIB
class CardSensors: UIView {
#IBOutlet weak var botName: UILabel!
#IBOutlet weak var sensorsCollectionView: UICollectionView!
var sensors = [[String: Any]]()
var viewModel: NewsFeedViewModel! {
didSet {
setUpView()
}
}
func setSensors(sensors: [[String: Any]]){
self.sensors = sensors
}
static func loadFromNib() -> CardSensors {
return Bundle.main.loadNibNamed("CardSensor", owner: nil, options: nil)?.first as! CardSensors
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func setupCollectionView(){
let nibName = UINib(nibName: "SensorCollectionViewCell", bundle: Bundle.main)
sensorsCollectionView.register(nibName, forCellWithReuseIdentifier: "SensorCollectionViewCell")
}
func setUpView() {
botName.text = viewModel.botName
}
}
extension CardSensors: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SensorCollectionViewCell", for: indexPath) as? SensorCell else {
return UICollectionViewCell()
}
cell.dateLabel.text = sensors[indexPath.row]["created_at"] as? String
cell.sensorType.text = sensors[indexPath.row]["type"] as? String
cell.sensorValue.text = sensors[indexPath.row]["value"] as? String
cell.sensorImage.image = UIImage(named: (sensors[indexPath.row]["type"] as? String)!)
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sensors.count
}
}
Im creating an object in another class like this but I want this to call the methods of the collectionView for it to load the info.
let sensorView = CardSensors.loadFromNib()
sensorView.sensors = sensores
sensorView.setupCollectionView()
The problem is that the collectionView methods are never being called. What can I do to call them from my other class?
You need to set the data souce
sensorsCollectionView.register(nibName, forCellWithReuseIdentifier: "SensorCollectionViewCell")
sensorsCollectionView.dataSource = self
sensorsCollectionView.reloadData()
Then inside your vc , make it an instance variable
var sensorView:CardSensors!
sensorView = CardSensors.loadFromNib()
sensorView.sensors = sensores
sensorView.setupCollectionView()

CollectionView not registering cells

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

UICollection View is not reloading while Navigating back?

I need to reload the collection view while navigating back to that particular view.
I tried implementing all the following things but its not working
1.calling self.collectionView.reloadData() in viewDidAppear()
2.calling self.collectionView.reloadData() in viewWillAppear()
even i tried calling viewDidLoad() in viewDidAppear but yet it is not working.
And checked the other same type of questions but it is not working...
the only way it works is by performing segue action but I dont think so it is efficent can anyone suggest me any idea??
enter code here class RoomDeviceListVC: UIViewController , UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout ,segueAction{
let functions = DataFunctionalities()
#IBOutlet weak var collectionView1: UICollectionView!
#IBOutlet weak var collectionView2: UICollectionView!
var horizontalBarLeadingConstraint : NSLayoutConstraint?
static var isVisible : Bool?
static var CollectionViewSection : Int!
static var reloadFlag : Int!
static var delegates : CollectionViewRotation? = nil
var uiFunctions = UIFunctions()
var selectedIndex = IndexPath(item: 0, section: 0)
var constant : CGFloat?
let headerView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
uiFunctions.setViewBackground(view: self.view)
self.navigationItem.hidesBackButton = true
setupHeader()
setupConstraints()
setupHorizontalBar()
collectionView2.clipsToBounds = true
RoomDeviceListVC.CollectionViewSection = selectedIndex.section
BottomViewRoomCell.delegates = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
// self.collectionView2.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.collectionView2.reloadData()
RoomDeviceListVC.isVisible = true
}
override func viewDidDisappear(_ animated: Bool) {
RoomDeviceListVC.isVisible = false
}
func setupHorizontalBar(){
let horizontalBar = UIView()
self.view.addSubview(horizontalBar)
horizontalBar.translatesAutoresizingMaskIntoConstraints = false
horizontalBar.backgroundColor = .white
horizontalBarLeadingConstraint = horizontalBar.leadingAnchor.constraint(equalTo: collectionView2.leadingAnchor)
horizontalBarLeadingConstraint?.isActive = true
horizontalBar.heightAnchor.constraint(equalToConstant: 3).isActive = true
NSLayoutConstraint(item: horizontalBar, attribute: .width, relatedBy: .equal, toItem: collectionView1, attribute: .width, multiplier: 0.5, constant: 1).isActive = true
horizontalBar.bottomAnchor.constraint(equalTo: collectionView2.topAnchor).isActive = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let menuName = ["Rooms" , "DeviceList"]
let color :[UIColor] = [.green , .blue]
if collectionView == collectionView1{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuBarCell", for: indexPath) as! MenuBarCell
cell.menuLabel.text = menuName[indexPath.section]
cell.menuLabel.textColor = UIColor.darkGray
DispatchQueue.main.async {
self.collectionView1.selectItem(at: self.selectedIndex, animated: false, scrollPosition: .centeredHorizontally)
}
return cell
}
else {
if indexPath.section == 0{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BottomViewRoomCell", for: indexPath) as! BottomViewRoomCell
cell.backgroundColor = .clear
cell.layer.cornerRadius = 9
cell.layer.masksToBounds = true
return cell
}
else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "BottomViewDeviceCell", for: indexPath) as! BottomViewDeviceCell
cell.backgroundColor = .clear
cell.layer.cornerRadius = 9
cell.layer.masksToBounds = true
return cell
}
}
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
if collectionView == collectionView1{
return CGSize(width: self.view.frame.width / 2, height: collectionView1.frame.height)
}
else{
return CGSize(width: self.view.frame.width, height: collectionView2.frame.height)
}
}
you may use UINavigationControllerDelegate to handle this situation.
add UINavigationControllerDelegate, sign self to the delegate of your collectionView's navigationController, and then put your personal function inside "willShow/didShow" delegate method. your personal function will be called when you pop back to the collectionView.
some method about cell display is not shown
class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UINavigationControllerDelegate{
private let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
// sign navigationController's delegate to collectionViewController itself.
navigationController?.delegate = self
}
// push view controller when cell is selected
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = CustomViewController()
navigationController?.pushViewController(vc, animated: true)
}
// call your personal function when this collection view controller shows back
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if viewController == self {
print("will show self")
}
}
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if viewController == self {
print("did show self")
}
}
}

create UICollectionViewController subclass and add as a childviewcontroller

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

Resources