Danny Pająk
Danny Pająk

Reputation: 73

CollectionView reloaddata() after value change

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

Answers (1)

Abdulaziz.Musaileekh
Abdulaziz.Musaileekh

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

Related Questions