iOS- UICollectionView horizontal scroll every cell within a UITableView - ios

I have a UITableView in which i have placed a UICollectionView and then reused this scenario for 10 or more cells.
What i want is when i scroll my UICollectionView each of the Cells of other collection view must also be scrolled at the same time synchronously.
MyViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet private weak var menuButton: UIButton!
#IBOutlet var dottedView: UIView!
var array1 = ["Indian Standard Time","Adelaide Standard Time"
,"Auckland Standard Time","Melbourne Standard Time",
"Manchester Standard Time","Paris Standard Time",
"Alaska Standard Time","Greenland Standard Time"
,"Sutherland Standard Time","Russia Standard Time"]
var array2 = ["desc","desc","desc","desc","desc"]
var array3 = ["Mon","Tue","Wed","Thu", "Fri","Sat","Sun"]
var array4 = ["12 2021","12 2021","12 2021","12 2021", "12 2021","12 2021","12 2021"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
#IBOutlet var maintableview: UITableView!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! TableViewCell
cell.tag = indexPath.row
cell.collectionview.tag = (100 * indexPath.section) + indexPath.row
cell.label1.text = array1[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 130
}
#IBAction func datetimeclick(_ sender: Any) {
let vc = storyboard?.instantiateViewController(identifier: "dateViewController") as! DateViewController
present(vc, animated: true)
}
#IBAction func addbtnclick(_ sender: Any) {
let vc = storyboard?.instantiateViewController(identifier: "timezonecontroller") as! TimeZoneViewController
present(vc, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dottedView.addDashedBorder()
let nextdate = Date().addDate()
let prevdate = Date().subtractDate()
let dayofweek = String().dayOfWeek(fDate: nextdate)
let tomorrowDate = String().dateOfWeek(fDate: nextdate)
let yesterdayDate = String().dateOfWeek(fDate: prevdate)
let year = String().getYear(fDate: Date())
let splitstr = dayofweek?.prefix(3)
print("tomdate",tomorrowDate!)
print("prevdate",yesterdayDate!)
print("year",year!)
print("dayofWeekCap", splitstr!)
menuButton.isUserInteractionEnabled = true
let interaction = UIContextMenuInteraction(delegate :self)
menuButton.backgroundColor = UIColor(hexString: "#1361E5")
menuButton.addInteraction(interaction)
}
override func viewDidLayoutSubviews() {
}
}
MyTableViewCell.swift
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var collectionview: UICollectionView!
#IBOutlet weak var collectioncell: UICollectionViewCell!
#IBOutlet weak var collectionText: UILabel!
var scrollToPosition = 0
var indexPosition = 0
extension TableViewCell : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 48
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let arrayString = timeSlot[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectioncell", for: indexPath)
let title = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 33))
indexPosition = indexPath.row
if indexPath.row >= scrollToPosition
{
title.textColor = UIColor.white
cell.contentView.backgroundColor = UIColor.systemBlue
}
else
{
title.textColor = UIColor.black
cell.layer.borderWidth = 1.0
cell.layer.borderColor = UIColor.darkGray.cgColor
cell.contentView.backgroundColor = UIColor.white
}
title.text = arrayString
title.textAlignment = NSTextAlignment.center
for subView in cell.contentView.subviews {
subView.removeFromSuperview()
}
cell.contentView.addSubview(title)
let now = Date()
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_IN")
formatter.dateFormat = "HH:mm" //for complete format EEEE dd MMMM YYYY HH:mm
let datetime = formatter.string(from: now)
let currentDate = createDateFromString(string: datetime)
for i in timeSlot.indices {
let arrayDate = createDateFromString(string: timeSlot[i])
if currentDate > arrayDate && flagDate == false {
flagDate = true
countToScroll = i
}
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 33)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
DispatchQueue.main.async {
for cell in self.collectionview.visibleCells {
let indexpath = self.collectionview.indexPath(for: cell)
self.indexPosition = indexpath!.row
self.collectionview.selectItem(at: IndexPath(row: self.indexPosition, section: 0), animated: true, scrollPosition: .centeredHorizontally)
}
}
}
}

Related

reload selected collection cell in independent time interval

I am trying to make countdown timer app with collectionView.
Features & Functions:
Each cell has own label and timer function.
CountDown timer will run if user touches a cell.
Time string have to updated as timer run.
I successfully build a timer in each cell but I'm stuck updating timeLabel (reload selected cell).
Please check the codes below and give me some hint.
class ListViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var recipeList: TimeRecipeList
var timer = Timer()
required init?(coder aDecoder: NSCoder) {
recipeList = TimeRecipeList()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let width = (view.frame.size.width - 10) / 2
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: width, height: width)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "AddItemSegue" {
if let addItemVC = segue.destination as? AddRecipeViewController {
addItemVC.delegate = self
}
}
}
}
extension ListViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return recipeList.item.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RecipeListCell", for: indexPath)
let item = recipeList.item[indexPath.row]
configureText(for: cell, with: item)
return cell
}
func configureText(for cell: UICollectionViewCell, with item: TimeRecipe) {
if let label = cell.viewWithTag(1) as? UILabel {
label.text = item.name
}
if let label = cell.viewWithTag(2) as? UILabel {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .abbreviated
let timeString = formatter.string(from: TimeInterval(item.time))
label.text = timeString
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RecipeListCell", for: indexPath)
let item = recipeList.item[indexPath.row]
let cellTimer = TimerControl()
cellTimer.second = item.time
cellTimer.timerRun()
configureText(for: cell, with: item)
}
class TimerControl {
var timer = Timer()
var second: Int = 0
func timerRun() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countDown), userInfo: nil, repeats: true)
}
#objc func countDown() {
//let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RecipeListCell", for: indexPath)
if second <= 1 {
timer.invalidate()
} else {
second -= 1
//collectionView.reloadItems(at: [indexPath])
//MARK: Reload selected cell
}
}
}
}
extension ListViewController: AddRecipeViewControllerDelegate {
func addRecipeViewControllerDidCancel(_ controller: AddRecipeViewController) {
dismiss(animated: true)
}
func addRecipeViewControllerDisSave(_ controller: AddRecipeViewController, didFinishAdding item: TimeRecipe) {
dismiss(animated: true)
let rowIndex = recipeList.item.count
recipeList.item.append(item)
let indexPath = IndexPath(row: rowIndex, section: 0)
let indexPaths = [indexPath]
collectionView.insertItems(at: indexPaths)
}
}
#objc func countDown(indexPath: IndexPath) {
if second <= 1 {
timer.invalidate()
} else {
second -= 1
//MARK: Reload selected cell
yourarray[indexPath.row] = newvalues
collectionView.reloadItems(at: [indexPath])
}
}

CollectionView in TableviewCell : Display data using Alamofire and SwiftyJSON with MVC model

I have a Collectionview in Tableviewcell. Using Alamofire and SwiftJSON i download data and store in my model project. I have 2 model file one for tableview, and one for collectionview
Model Project for Tableview:
class Matchs {
var matchTime : String
var matchMbs : Int
var matchLeague : String
var matchCode : Int
var matchHomeTeam : String
var matchAwayTeam : String
init(time : String, mbs : Int, league : String, code : Int, homeTeam : String, awayTeam : String ) {
matchTime = time
matchMbs = mbs
matchLeague = league
matchCode = code
matchHomeTeam = homeTeam
matchAwayTeam = awayTeam
}
}
Model Project for Collectionview:
class MatchsOdds {
var homeWin : Double
var awayWin : Double
var draw : Double
var under : Double
var over : Double
var oddResult = ["1","0","2","Under","Over"]
init(homeWin: Double, awayWin : Double, draw : Double, under : Double, over : Double) {
self.homeWin = homeWin
self.awayWin = awayWin
self.draw = draw
self.under = under
self.over = over
}
}
And here is the Viewcontroller code :
class MainViewController: UIViewController {
#IBOutlet weak var tableview: UITableView!
let MATCH_URL = "somelink"
var match : [Matchs] = []
var matchOdd : [MatchsOdds] = []
#IBOutlet weak var totalMatchLabel: UILabel!
#IBOutlet weak var totalOddsLabel: UILabel!
fileprivate func getMatchData(url:String) {
Alamofire.request(url).responseJSON {
response in
if response.result.isSuccess {
print("I got the match data")
let matchJSON : JSON = JSON(response.result.value!)
self.getMatch(json: matchJSON)
}else {
print("Match Unavailable")
}
self.tableview.reloadData()
}
}
fileprivate func getMatch(json: JSON) {
if let matchs = json["data"]["b"]["f"]["list"].array {
matchs.forEach { (arg0) in
let events = arg0
let tournamentName = events["tournamentName"].stringValue
let homeTeam = events["homeTeam"].stringValue
let mbs = events["mbs"].intValue
let matchCode = events["matchCode"].intValue
let matchTime : String = events["eventHour"].stringValue
let awayTeam = events["awayTeam"].stringValue
let matchTimeFirst5 = matchTime.prefix(5)
let allMatch = Matchs(time: String(matchTimeFirst5), mbs: mbs, league: tournamentName, code: matchCode, homeTeam: homeTeam, awayTeam: awayTeam)
let homeWin = events["oddList"][0]["v"].doubleValue
let draw = events["oddList"][1]["v"].doubleValue
let awaywin = events["oddList"][2]["v"].doubleValue
let under = events["oddList"][3]["v"].doubleValue
let over = events["oddList"][4]["v"].doubleValue
let fiveOdds = MatchsOdds(homeWin: homeWin, awayWin: awaywin, draw: draw, under: under, over: over)
match.append(allMatch)
matchOdd.append(fiveOdds)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
getMatchData(url: MATCH_URL)
}
override func viewDidAppear(_ animated: Bool) {
}
}
extension MainViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return match.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let matcCell = match[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "oddsCell") as! OddsTableViewCell
tableView.separatorStyle = .none
cell.matchToMatch(matcs: matcCell)
tableview.allowsSelection = false
cell.collectionview.tag = indexPath.row
cell.collectionview.reloadData()
cell.matchOddCV = matchOdd
return cell
}
}
No problem from here. I cat write data to Tableviewcell labels. But when i try the matchOdds data to Collectionview cell label. I only got first data which is repeating like this:
Here is the code for Tableviewcell :
class OddsTableViewCell: UITableViewCell {
#IBOutlet weak var reloadButton: RoundedButton!
#IBAction func reloadBottonPressed() {
print("reloadDataPressed")
}
var matchOddCV : [MatchsOdds]!
#IBOutlet weak var collectionview: UICollectionView!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var mbsLabel: UILabel! {
didSet{
mbsLabel.layer.cornerRadius = 5
mbsLabel.layer.masksToBounds = true
}
}
#IBOutlet weak var leageLabel: UILabel!
#IBOutlet weak var matchCodeLabel: UILabel!
#IBOutlet weak var homeVsAwayLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
collectionview.dataSource = self
collectionview.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func matchToMatch (matcs : Matchs) {
timeLabel.text = matcs.matchTime
mbsLabel.text = String(matcs.matchMbs)
leageLabel.text = matcs.matchLeague
matchCodeLabel.text = String(matcs.matchCode)
homeVsAwayLabel.text = "\(matcs.matchHomeTeam) - \(matcs.matchAwayTeam)"
}
}
extension OddsTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width/5, height: collectionView.bounds.height)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "oddsCollectionViewCell", for: indexPath) as! OddsCollectionViewCell
let matchCell = matchOddCV[indexPath.row]
if indexPath.row == 0 {
cell.matchResultLabel.text = matchCell.oddResult[0]
cell.matchOddsLabel.text = String(matchCell.homeWin)
}else if indexPath.row == 1{
cell.matchResultLabel.text = matchCell.oddResult[1]
cell.matchOddsLabel.text = String(matchCell.draw)
}else if indexPath.row == 2 {
cell.matchResultLabel.text = matchCell.oddResult[2]
cell.matchOddsLabel.text = String(matchCell.awayWin)
}else if indexPath.row == 3 {
cell.matchResultLabel.text = matchCell.oddResult[3]
cell.matchOddsLabel.text = String(matchCell.under)
}else if indexPath.row == 4 {
cell.matchResultLabel.text = matchCell.oddResult[4]
cell.matchOddsLabel.text = String(matchCell.over)
}
cell.matchOddsLabel.layer.borderWidth = 0.5
cell.matchOddsLabel.layer.borderColor = UIColor.lightGray.cgColor
cell.matchResultLabel.layer.borderWidth = 0.5
cell.matchResultLabel.layer.borderColor = UIColor.lightGray.cgColor
return cell
}
}
Collectionviewcell code :
class OddsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var matchResultLabel: UILabel!
#IBOutlet weak var matchOddsLabel: UILabel!
}
According to the table's cellForRowAt
cell.collectionview.reloadData()
cell.matchOddCV = matchOdd
you assign the same array for all the collections inside all the table cells , what you need is to create an array property inside each Matchs object and do
let matcCell = match[indexPath.row]
cell.matchOddCV = matcCell.odds // create odds array and assign data for each object
cell.collectionview.reloadData()
As I can see, you are reloading the collectionview before setting the new list matchOddCV.
So you should do:
// assign the new list
cell.matchOddCV = matchOdd
// reload colllectionview
cell.collectionview.reloadData()

UICollectionView showing unloaded xib and not updating the content inside using swift

I have a collection view which is loaded from a .xib file. When the view opens sometimes the collection view will have loaded the content and other times it does not load any content into the cell causing just the .xib to be shown. Other times the .xib doesn't even show either. However, I don't understand why this is happening. When clicking on the cell, a new viewController opens with a detailed view which has the content loaded so the cell obviously knows what is suppose to be shown.
var currentUser: User!
var listCategories: [String] = ["Friends Lists", "Friends", "People"]
var lists = [Media]()
in viewDidLoad:
collectionView.register(UINib(nibName: "ListCell2.0", bundle: nil), forCellWithReuseIdentifier: Storyboard.listCell)
collectionView.reloadData()
observeMedia()
observeMedia():
func observeMedia() {
Media.observeNewMedia { (media) in
if !self.lists.contains(media) {
self.lists.insert(media, at: 0)
self.collectionView.reloadData()
}
}
}
viewWillAppear:
override func viewWillAppear(_ animated: Bool) {
observeMedia()
}
collectionView Methods:
extension HomeViewController
{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return listCategories.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return lists.count
}else{
return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Storyboard.listCell, for: indexPath) as! ListCell
cell.layer.applySketchShadow(color: UIColor.black, alpha: 0.08, x: 0, y: 0, blur: 10, spread: 0)
cell.layer.cornerRadius = 20
cell.layer.masksToBounds = false
cell.currentUser = self.currentUser
cell.media = self.lists[indexPath.item]
cell.mainView.setGradientBackground(colours: self.getColourFromTag(tag: self.lists[indexPath.item].tag))
return cell
}
//section header view
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
{
let sectionHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: Storyboard.sectionHeader, for: indexPath) as! SectionHeaderView
let category = listCategories[indexPath.section]
sectionHeaderView.sectionTitle = category
return sectionHeaderView
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: Storyboard.showListDetailSegue, sender: nil)
}
the CollectionView Cell
import UIKit
import Foundation
import SAMCache
class ListCell: UICollectionViewCell {
#IBOutlet weak var nameView: UIView!
#IBOutlet weak var mainView: UIView!
#IBOutlet weak var nameButton: UIButton!
#IBOutlet weak var profileImageView: UIImageView!
//#IBOutlet weak var tagLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var listTitle: UILabel!
#IBOutlet weak var boughtLabel: UILabel!
#IBOutlet weak var boughtProgress: UIProgressView!
var numOfItems = 0
var numOfBought = 0
var counter: Double = 0{
didSet{
boughtProgress.isHidden = false
let fractionalProgress = Float(counter)
boughtProgress.setProgress(fractionalProgress, animated: true)
}
}
var currentUser: User!
var media: Media! {
didSet{
if currentUser != nil{
self.updateUI()
}
}
}
var cache = SAMCache.shared()
func updateUI(){
let profileImageKey = "\(media.createdBy.uid)-profileImage"
if let image = cache?.object(forKey: profileImageKey) as? UIImage {
self.profileImageView.image = image
}else{
media.createdBy.downloadProfilePicture { [weak self] (image, error) in
if let image = image {
self?.profileImageView.image = image
self?.cache?.setObject(image, forKey: profileImageKey)
}else if error != nil {
print(error)
}
}
}
mainView.layer.cornerRadius = 20
mainView.layer.masksToBounds = true
//profile image
profileImageView.layer.cornerRadius = profileImageView.bounds.height / 2.0
profileImageView.layer.masksToBounds = true
//name
nameButton.setTitle("\(media.createdBy.firstName) \(media.createdBy.lastName)", for: [])
nameView.layer.cornerRadius = 20
nameView.layer.masksToBounds = true
//date
dateLabel.text = "\(convertDateFormatter(theDate: media.dueAt))"
dateLabel.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.3)
dateLabel.textColor = UIColor.white
dateLabel.layer.cornerRadius = dateLabel.bounds.height / 2.0
dateLabel.layer.masksToBounds = true
//title
listTitle.text = "\(media.title)"
//progress
numOfItems = media.items.count
print("num of items \(media.items.count)")
counter = Double(numOfBought)/Double(numOfItems)
boughtLabel.text = "\(numOfBought)/\(numOfItems) Bought"
boughtProgress.layer.cornerRadius = boughtProgress.bounds.height / 2.0
boughtProgress.layer.masksToBounds = true
}
#IBAction func arrowDidTap(){
print("arrow tapped")
print(media.tag)
}
func convertDateFormatter(theDate: String) -> String
{
print(theDate)
let newFormat = DateFormatter()
newFormat.dateFormat = "dd/MM/yyyy"
let dueDate = newFormat.date(from: theDate)
newFormat.dateFormat = "dd MMM yy"
print(newFormat.string(from: dueDate!))
return newFormat.string(from: dueDate!)
}
The first image shows when the view first loads. this is just what is shown in the .xib, however, the gradient has loaded, not the content
the second image shows how it should look. This is after scrolling through the view

How to draw on collectionView cells using UICollectionView's selectItem?

I added the repo I am working here:
https://github.com/AlexMarshall12/singleDayTimeline/tree/master/singleDayTimeline
Basically I have 900 collectionView cells (with a custom XIB layout).
let cellIdentifier = "DayCollectionViewCell"
class ViewController: UIViewController, UICollectionViewDataSource,UICollectionViewDelegate {
#IBOutlet weak var button: UIButton!
var dates = [Date?]()
var startDate: Date?
#IBOutlet weak var daysCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
daysCollectionView.register(UINib.init(nibName: "DayCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: cellIdentifier)
let allDates = Helper.generateRandomDate(daysBack: 900, numberOf: 10)
self.dates = allDates.sorted(by: {
$0!.compare($1!) == .orderedAscending
})
startDate = self.dates.first! ?? Date()
daysCollectionView.delegate = self
daysCollectionView.dataSource = self
// Do any additional setup after loading the view, typically from a nib.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 900
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = daysCollectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! DayCollectionViewCell
let cellDate = Calendar.current.date(byAdding: .day, value: indexPath.item, to: self.startDate!)
if Calendar.current.component(.day, from: cellDate!) == 15 {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM"
let monthString = dateFormatter.string(from: cellDate!)
cell.drawMonth(month: monthString)
}
if Calendar.current.component(.day, from: cellDate!) == 1 && Calendar.current.component(.month, from: cellDate!) == 1 {
print("drawYEAR")
cell.drawYear(year:Calendar.current.component(.year, from: cellDate!))
}
if self.dates.contains(where: { Calendar.current.isDate(cellDate!, inSameDayAs: $0!) }) {
print("same")
cell.backgroundColor = UIColor.red
} else {
print("not me")
//cell.backgroundColor = UIColor.lightGray
}
return cell
}
// func collectionView(_ collectionView: UICollectionView,
// layout collectionViewLayout: UICollectionViewLayout,
// sizeForItemAt indexPath: IndexPath) -> CGSize {
// return CGSize(width: 2, height: daysCollectionView.bounds.size.height/2 )
// }
#IBAction func buttonPressed(_ sender: Any) {
let randomIndex = Int(arc4random_uniform(UInt32(self.dates.count)))
let randomDate = self.dates[randomIndex]
let daysFrom = randomDate?.days(from: self.startDate!)
let indexPath = IndexPath(row: daysFrom!, section: 0)
// if let cell = daysCollectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as DayCollectionViewCell? {
// print("found it")
// } else {
// print("didn't find it")
// }
daysCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredHorizontally)
daysCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
Then here is the cell:
class DayCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var arrowImage: UIImageView!
override var isSelected: Bool{
didSet{
arrowImage.isHidden = !isSelected
}
}
override func awakeFromNib() {
super.awakeFromNib()
arrowImage.isHidden = true
}
override func prepareForReuse() {
self.backgroundColor = UIColor.clear
}
func drawMonth(month: String){
}
func drawYear(year: Int){
}
}
It looks like this:
So the plan is that when that button is pressed, you can see in the #IBAction func buttonPressed that a random date is chosen and scrolled to, then that cell is selected in the collectionView. Then that makes the cell's arrow drawn with arrowImage.isHidden = !isSelected in the override var isSelected function.
Currently, this works almost perfectly. The arrow is redrawn under the selected cell EXCEPT when the new index which gets randomly selected is far enough away from the current index. My theory is that if the index difference is big enough, the next cell hasn't been loaded/dequeued yet and thus isSelected is never called. However I am not sure still why its not working properly
1- Add a reloadCell function to change ui of cell. Then you should remove override var isSelected and arrowImage.isHidden = true from awakeFromNib function.
func reloadCell(_ isSelected:Bool){
arrowImage.isHidden = !isSelected
}
2- You should define a variable on ViewController.swift class private var selectedIndexPath: IndexPath? and then you should add this code for to check if arrow is hidden or not.
if let selectedRow = selectedIndexPath {
cell.reloadCell(selectedRow == indexPath)
} else {
cell.reloadCell(false)
}
3- And if you change your button action function like this below, it would be worked.
#IBAction func buttonPressed(_ sender: Any) {
let randomIndex = Int(arc4random_uniform(UInt32(self.dates.count)))
let randomDate = self.dates[randomIndex]
let daysFrom = randomDate?.days(from: self.startDate!)
let indexPath = IndexPath(row: daysFrom!, section: 0)
self.selectedIndexPath = indexPath;
daysCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
daysCollectionView.reloadData()
}
All codes here.
ViewController.swift
import UIKit
let cellIdentifier = "DayCollectionViewCell"
class ViewController: UIViewController, UICollectionViewDataSource,UICollectionViewDelegate {
#IBOutlet weak var button: UIButton!
var dates = [Date?]()
var startDate: Date?
private var selectedIndexPath: IndexPath?
#IBOutlet weak var daysCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
daysCollectionView.register(UINib.init(nibName: "DayCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: cellIdentifier)
let allDates = Helper.generateRandomDate(daysBack: 900, numberOf: 10)
self.dates = allDates.sorted(by: {
$0!.compare($1!) == .orderedAscending
})
startDate = self.dates.first! ?? Date()
daysCollectionView.delegate = self
daysCollectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 900
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = daysCollectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! DayCollectionViewCell
let cellDate = Calendar.current.date(byAdding: .day, value: indexPath.item, to: self.startDate!)
if let selectedRow = selectedIndexPath {
cell.reloadCell(selectedRow == indexPath)
} else {
cell.reloadCell(false)
}
if Calendar.current.component(.day, from: cellDate!) == 15 {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM"
let monthString = dateFormatter.string(from: cellDate!)
cell.drawMonth(month: monthString)
}
if Calendar.current.component(.day, from: cellDate!) == 1 && Calendar.current.component(.month, from: cellDate!) == 1 {
print("drawYEAR")
cell.drawYear(year:Calendar.current.component(.year, from: cellDate!))
}
if self.dates.contains(where: { Calendar.current.isDate(cellDate!, inSameDayAs: $0!) }) {
print("same")
cell.backgroundColor = UIColor.red
} else {
print("not me")
//cell.backgroundColor = UIColor.lightGray
}
return cell
}
#IBAction func buttonPressed(_ sender: Any) {
let randomIndex = Int(arc4random_uniform(UInt32(self.dates.count)))
let randomDate = self.dates[randomIndex]
let daysFrom = randomDate?.days(from: self.startDate!)
let indexPath = IndexPath(row: daysFrom!, section: 0)
self.selectedIndexPath = indexPath;
//daysCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredHorizontally)
daysCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
daysCollectionView.reloadData()
}
}
DayCollectionViewCell.swift
import UIKit
class DayCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var arrowImage: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func prepareForReuse() {
self.backgroundColor = UIColor.clear
}
func drawMonth(month: String){
}
func drawYear(year: Int){
}
func reloadCell(_ isSelected:Bool){
arrowImage.isHidden = !isSelected
}
}

UICollectionView with Custom Cell Breaking UICollectionViewDataSource Protocol

I am trying to use a UICollectionView with a custom cell. My ViewController inherits from UICollectionViewDataSource as below:
import UIKit
import Parse
class DressingRoomViewController: UIViewController,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource {
That inheritance is causing me to break a protocol of UICollectionViewDataSource with my UICollectionView function that creates and returns the custom cell. This is the function:
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath)
-> CustomCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
identifier,forIndexPath:indexPath) as! CustomCell
let dressingRoomIcons: [DressingRoomIcon] =
dataSource.dressingRoomIcons
let dressingRoomIcon = dressingRoomIcons[indexPath.row]
var imageView: MMImageView =
createIconImageView(dressingRoomIcon.name!)
cell.setImageV(imageView)
return cell
}
So before compilation the error is shown in the IDE. How do I get around this error? Here are the two errors I am experiencing:
Type 'DressingRoomViewController' does not conform to protocol
'UICollectionViewDataSource'
Cannot assign a value of type 'DressingRoomViewController' to a value
of type 'UICollectionViewDataSource?'
Here is the whole ViewController:
import UIKit
import Parse
class DressingRoomViewController: UIViewController,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource {
#IBOutlet weak var MirrorImageView: UIImageView!
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
let identifier = "cellIdentifier"
let dataSource = DataSource()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
let cellSpacing: CGFloat = 5
let cellsPerRow: CGFloat = 6
let numberOfItems = 12
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
}
override func viewDidAppear(animated: Bool) {
let cellSize = (collectionView.collectionViewLayout
.collectionViewContentSize().width
/ cellsPerRow)
- (cellSpacing)
layout.itemSize = CGSize(width: cellSize, height: cellSize)
layout.minimumInteritemSpacing = cellSpacing
layout.minimumLineSpacing = cellSpacing
layout.scrollDirection = UICollectionViewScrollDirection.Horizontal
collectionView.collectionViewLayout = layout
self.heightConstraint.constant = cellSize
self.view.layoutIfNeeded()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue( segue: UIStoryboardSegue,
sender: AnyObject?) {
if (segue.identifier == "dressingRoom2MyOutfits") {
let myOutfitsViewController = segue.destinationViewController
as! MyOutfitsViewController
} else if (segue.identifier == "dressingRoom2StickerPicker") {
let myStickerPickerController = segue.destinationViewController
as! StickerPickerViewController
}
}
func imageTapped(sender: UITapGestureRecognizer) {
var imageView = sender.view as! MMImageView
println(imageView.fname)
performSegueWithIdentifier( "dressingRoom2StickerPicker",
sender: imageView)
}
}
// MARK:- UICollectionViewDataSource Delegate
extension DressingRoomViewController : UICollectionViewDataSource {
func numberOfSectionsInCollectionView(
collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath)
-> CustomCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
identifier,forIndexPath:indexPath) as! CustomCell
let dressingRoomIcons: [DressingRoomIcon] =
dataSource.dressingRoomIcons
let dressingRoomIcon = dressingRoomIcons[indexPath.row]
var imageView: MMImageView =
createIconImageView(dressingRoomIcon.name!)
cell.setImageV(imageView)
return cell
}
func createIconImageView(name: String) -> MMImageView{
var imageView :MMImageView =
MMImageView(frame:CGRectMake( 0,
0,
(collectionView.collectionViewLayout
.collectionViewContentSize().width / cellsPerRow)
- (cellSpacing),
(collectionView.collectionViewLayout
.collectionViewContentSize().width / cellsPerRow)
- (cellSpacing)))
imageView.contentMode = UIViewContentMode.ScaleAspectFit
imageView.image = UIImage(named: name)
imageView.setName(name)
imageView.backgroundColor = UIColor.clearColor()
imageView.userInteractionEnabled = true
var tapGestureRecognizer =
UITapGestureRecognizer(target: self, action: "imageTapped:")
tapGestureRecognizer.numberOfTapsRequired = 1
imageView.addGestureRecognizer(tapGestureRecognizer)
return imageView
}
}
EDIT: Here is my CustomCell:
import Foundation
import UIKit
class CustomCell: UICollectionViewCell {
var imageView = MMImageView()
func setImageV(IV: MMImageView) {
self.imageView = IV
}
}
Replace this Code
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath)
-> UICollectionViewCell {
Instead of below this :
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath)
-> CustomCell {
You can return custom cell but you can't change return type of any DataSource or Delegate method.

Resources