Reputation: 73
I have a problem updating my CollectionView after values in an array changed. I made a short gif of my problem.
As you can see I can add goals for both teams in my soccer app. When I press the game statistic button for the first time, the result (e.g. 2:1) in the statistic view sliding in from the bottom, is correct. But as the game goes on and the home team scores again, the statistics won’t change.
I have tried several ways to call reloaddata() to update my collectionView, with no success so far. So hopefully someone can get me on the right track.
Thank you very much in advance.
GameStatistics View Controller - GameStatistics.swift
import UIKit
class StatisticTitle: NSObject {
let name: String
init(name: String) {
self.name = name
}
}
class HomeTeamValue: NSObject {
var value: Int
init(value: Int) {
self.value = value
}
}
class AwayTeamValue: NSObject {
var value: Int
init(value: Int) {
self.value = value
}
}
class GameStatistics: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let blackView = UIView()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}()
let cellId = "cellId"
let sectionHeader = "sectionHeader"
let sectionFooter = "sectionFooter"
let cellHeight: CGFloat = 40
let headerHeight: CGFloat = 80
let footerHeight: CGFloat = 50
let cellSpacing: CGFloat = 0
let statisticTitles: [StatisticTitle] = {
return[
StatisticTitle(name: "Goals"), //Tore
StatisticTitle(name: "Shots on Target"), //Schüsse aufs Tor
StatisticTitle(name: "Shots off Target"), //Schüsse neben das Tor
StatisticTitle(name: "Free Kicks"), //Freistöße
StatisticTitle(name: "Corner Kicks"), //Eckbälle
StatisticTitle(name: "Fouls"), //Fouls
StatisticTitle(name: "Offside / Centerline"), //Abseits / Mittellinie
StatisticTitle(name: "Cautions")] //Cautions Strafen
}()
lazy var homeTeamValues: [HomeTeamValue] = {
[ HomeTeamValue(value: homeGoals),
HomeTeamValue(value: 0),
HomeTeamValue(value: 0),
HomeTeamValue(value: 0),
HomeTeamValue(value: 0),
HomeTeamValue(value: 0),
HomeTeamValue(value: 0),
HomeTeamValue(value: 0)
]
}()
lazy var awayTeamValues: [AwayTeamValue] = {
[ AwayTeamValue(value: awayGoals),
AwayTeamValue(value: 0),
AwayTeamValue(value: 0),
AwayTeamValue(value: 0),
AwayTeamValue(value: 0),
AwayTeamValue(value: 0),
AwayTeamValue(value: 0),
AwayTeamValue(value: 0)
]
}()
func showStatistics() {
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
window.addSubview(blackView)
window.addSubview(collectionView)
// Dynamic Height of Collection View
let value: CGFloat = CGFloat(statisticTitles.count)
let height: CGFloat = value * cellHeight + (value - 1) * cellSpacing + headerHeight + footerHeight
let y = window.frame.height - height
blackView.frame = window.frame
collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
blackView.alpha = 0
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.collectionView.frame = CGRect(x: 0, y: y, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}, completion: nil)
}
}
@objc func handleDismiss() {
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(x: 0, y: window.frame.height, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return statisticTitles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! GameStatisticCell
let title = statisticTitles[indexPath.item]
cell.statisticTitle = title
let homeValue = homeTeamValues[indexPath.item]
cell.homeTeamValue = homeValue
let awayValue = awayTeamValues[indexPath.item]
cell.awayTeamValue = awayValue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: cellHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return cellSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return cellSpacing
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionHeader, for: indexPath)
return supplementaryView
case UICollectionElementKindSectionFooter:
let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionFooter, for: indexPath)
return supplementaryView
default:
fatalError("Unexpected element kind")
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: headerHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: footerHeight)
}
override init() {
super.init()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(GameStatisticCell.self, forCellWithReuseIdentifier: cellId)
collectionView.register(GameStatisticHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: sectionHeader)
collectionView.register(GameStatisticFooter.self, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: sectionFooter)
}
}
GameStatistic Cell - GameStatisticCell.swift
import UIKit
class GameStatisticCell: BaseCell {
let statisticTitleLabel: UILabel = {
let label = UILabel()
label.text = "Shots on Goal"
label.textColor = ColorCodes.darkGray
label.textAlignment = .center
label.font = UIFont(name: "HelveticaNeue-Medium", size: 12)
return label
}()
var statisticTitle: StatisticTitle? {
didSet { statisticTitleLabel.text = statisticTitle?.name }
}
let homeTeamStatistic: UILabel = {
let label = UILabel()
label.text = String(12)
label.textColor = ColorCodes.darkGray
label.textAlignment = .right
label.font = UIFont(name: "HelveticaNeue-CondensedBold", size: 20)
return label
}()
var valueHomeTeam: Int = 1
var homeTeamValue: HomeTeamValue? {
didSet {
homeTeamStatistic.text = homeTeamValue?.value.description
valueHomeTeam = (homeTeamValue?.value)!
print(valueHomeTeam)
}
}
let awayTeamStatistic: UILabel = {
let label = UILabel()
label.text = String(2)
label.textColor = ColorCodes.darkGray
label.textAlignment = .left
label.font = UIFont(name: "HelveticaNeue-CondensedBold", size: 20)
return label
}()
var valueAwayTeam: Int = 1
var awayTeamValue: AwayTeamValue? {
didSet {
awayTeamStatistic.text = awayTeamValue?.value.description
valueAwayTeam = (awayTeamValue?.value)!
print(valueAwayTeam)
}
}
var homeTeamStatisticBar: UIView = {
let view = UIView()
view.backgroundColor = UIColor.lightGray
return view
}()
var awayTeamStatisticBar: UIView = {
let view = UIView()
view.backgroundColor = UIColor.darkGray
return view
}()
override func setupCell() {
super.setupCell()
backgroundColor = .white
addSubview(statisticTitleLabel)
addSubview(homeTeamStatistic)
addSubview(awayTeamStatistic)
addSubview(homeTeamStatisticBar)
addSubview(awayTeamStatisticBar)
let statisticValueSum = valueHomeTeam + valueAwayTeam
print(statisticValueSum)
let barWidthHome = CGFloat((Int(pitchWidth! / 2) - 40) * valueHomeTeam / statisticValueSum)
let barWidthAway = CGFloat((Int(pitchWidth! / 2) - 40) * valueAwayTeam / statisticValueSum)
let barCenter = pitchWidth! / 2
statisticTitleLabel.anchor(top: nil, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 4, paddingRight: 0, width: 0, height: 0)
addConstraint(NSLayoutConstraint(item: statisticTitleLabel, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
homeTeamStatisticBar.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: barCenter, width: barWidthHome, height: 16)
awayTeamStatisticBar.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 4, paddingLeft: barCenter, paddingBottom: 0, paddingRight: 0, width: barWidthAway, height: 16)
homeTeamStatistic.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
awayTeamStatistic.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
}
}
Statistic Button - TimerBar.swift
var homeGoals: Int = 0 {
didSet {
GameStatistics().collectionView.reloadData()
}
}
var awayGoals: Int = 0 {
didSet {
GameStatistics().collectionView.reloadData()
}
}
class TimerBar: UIView {
[...] //more Code
let homeGoalsLabel: UILabel = {
let label = UILabel()
label.text = String(homeGoals)
label.textColor = UIColor.white
label.textAlignment = .right
label.font = UIFont(name: "HelveticaNeue-CondensedBold", size: 34)
return label
}()
let awayGoalsLabel: UILabel = {
let label = UILabel()
label.text = String(awayGoals)
label.textColor = UIColor.white
label.textAlignment = .left
label.font = UIFont(name: "HelveticaNeue-CondensedBold", size: 34)
return label
}()
[...] //more Code
//Handle Menu Buttons
let changeLineUp = ChangeLineUp()
var delegate: LineUpDelegate?
var pitchLayout: String = "Test"
@objc func lineUpButtonTapped() {
changeLineUp.showLineUps()
changeLineUp.delegate = self
}
let gameStatistics = GameStatistics()
@objc func statButtonTapped() {
gameStatistics.showStatistics()
}
@objc func homeTeamButtonTapped(_ sender:UIButton!) {
homeGoals = homeGoals + 1
homeGoalsLabel.text = String(homeGoals)
}
@objc func awayTeamButtonTapped(_ sender:UIButton!) {
awayGoals = awayGoals + 1
awayGoalsLabel.text = String(awayGoals)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
...
Main Pitch View - PitchViewController.swift
import UIKit
let lineup7_321: UICollectionViewLayout = LineUp7_3_2_1()
public var pitchCollectionView: UICollectionView? = {
var layout: UICollectionViewLayout = lineup7_321
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
//cv.dataSource = self
//cv.delegate = self
return cv
}()
class PitchViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellId1 = "PlayersCell"
let cellId2 = "SubstituteCell"
let timerBar: TimerBar = {
let tb = TimerBar()
return tb
}()
//Just for testing
let sections: [String] = ["Players", "Substitutes"]
let players: [String] = ["player1", "player2", "player3", "player4", "player5", "player6", "player7"]
let substitutes: [String] = ["player8", "player9", "player10", "player12", "player13"]
var sectionData: [Int: [String]] = [:]
//Swap Players
var alwaysSwapWithOrigin: Bool!
var placementTimer: DispatchSourceTimer!
var longPress: UILongPressGestureRecognizer!
var movingPlayer: (origin:IndexPath?,lifted:IndexPath?,placement:IndexPath?,previous:IndexPath?)
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.titleView = UIImageView(image: #imageLiteral(resourceName: "overtime"))
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.tintColor = UIColor.white
setupTimerBar()
setupPitchCollectionView()
setupSwapPlayers()
}
func setupTimerBar() {
view.addSubview(timerBar)
timerBar.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 135)
}
func setupPitchCollectionView() {
pitchCollectionView?.register(PlayersCell.self, forCellWithReuseIdentifier: cellId1)
pitchCollectionView?.register(SubstituteCell.self, forCellWithReuseIdentifier: cellId2)
pitchCollectionView?.delegate = self
pitchCollectionView?.dataSource = self
pitchCollectionView?.isScrollEnabled = false
view.addSubview(pitchCollectionView!)
pitchCollectionView?.backgroundColor = ColorCodes.lightGreen
pitchCollectionView?.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 135, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
sectionData = [0: players, 1: substitutes]
}
func setupSwapPlayers() {
alwaysSwapWithOrigin = true
longPress = UILongPressGestureRecognizer(target: self, action:#selector(swapPlayersCells))
longPress.minimumPressDuration = 0.20
pitchCollectionView?.addGestureRecognizer(longPress)
let queue = DispatchQueue(label: "com.overtime")
placementTimer = DispatchSource.makeTimerSource(flags: [],queue:queue)
placementTimer.schedule(deadline: .now(), repeating:.milliseconds(250))
placementTimer.setEventHandler(handler: playersPositionUpdate)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return sections.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (sectionData[section]?.count)!
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.section {
case 0:
let playerCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId1, for: indexPath) as! PlayersCell
playerCell.playerImage.image = UIImage(named: self.players[indexPath.row])
playerCell.playerName.text = self.players[indexPath.row].capitalized
return playerCell
case 1:
let substituteCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId2, for: indexPath) as! SubstituteCell
substituteCell.substituteImage.image = UIImage(named: self.substitutes[indexPath.row])
substituteCell.substituteName.text = self.substitutes[indexPath.row].capitalized
return substituteCell
default:
fatalError("Error")
}
}
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
// required for interactive movement
}
func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath {
// return the index path of selected to prevent other cells reshuffling whilst moving cell around
return movingPlayer.lifted!
}
}
Upvotes: 2
Views: 2016
Reputation: 325
I didn't see any declaration for Home/Away values, this is a sample I hope you can get the idea from it
private var homeGoals : Int = 0 {
didSet {
collectionView.reloadData()
}
}
private var awayGoals : Int = 0 {
didSet {
collectionView.reloadData()
}
}
@objc func homeTeamButtonTapped(_ sender: UIButton) {
homeGoals = homeGoals + 1
}
As you can see here any new value for homeGoals
variable collectionView will be reloaded immediately and you have to use in your cell
Upvotes: 2