Search code examples
swiftloopsfor-loopuilabel

Dynamically Create Labels using Swift


I'm writing a score keeping app for a game and I'd like the players to be able to record multiple stats for games that could have 4, 6, 8 or even more players. I set up my labels as shown below:

    var playerOneReboundMadeLabel = UILabel()
    var playerTwoReboundMadeLabel = UILabel()
    var playerThreeReboundMadeLabel = UILabel()
    var playerFourReboundMadeLabel = UILabel()
    var playerFiveReboundMadeLabel = UILabel()
    var playerSixReboundMadeLabel = UILabel()
    var playerOneScoreLabel = UILabel()
    var playerTwoScoreLabel = UILabel()
    var playerThreeScoreLabel = UILabel()
    var playerFourScoreLabel = UILabel()
    var playerFiveScoreLabel = UILabel()
    var playerSixScoreLabel = UILabel()
    var numberOfPlayers = 6

override func viewDidLoad() {

var playerReboundAttemptLabelArray = [playerOneReboundAttemptsLabel, playerTwoReboundAttemptsLabel, playerThreeReboundAttemptsLabel, playerFourReboundAttemptsLabel, playerFiveReboundAttemptsLabel, playerSixReboundAttemptsLabel]

for player in 1...numberOfPlayers {
            let label = UILabel(frame: CGRect(x: playerOneScoreLabel.frame.maxX, y: playerScoreLabelArray[player-1].frame.minY, width: viewWidth*columnThreeWidth, height: viewHeight*labelHeight))
            label.tag = player+100
            label.backgroundColor = UIColor(red: 255/255, green: 197/255, blue: 142/255, alpha: 1.0)
            label.textAlignment = .center
            label.font = UIFont(name: "AmericanTypewriter-Bold", size: 23)
            label.text = "\(playerReboundAttemptsArray[player-1])"
            label.adjustsFontSizeToFitWidth = true
            label.isUserInteractionEnabled = true
            playerStatsView.addSubview(label)
            playerReboundAttemptLabelArray[player-1] = view.viewWithTag(player+100) as! UILabel
        }
        
        //playerThreeReboundAttemptsLabel = view.viewWithTag(106) as! UILabel
        playerThreeReboundAttemptsLabel.text = "54"
}

When the "playerThreeReboundAttemptsLabel = view.viewWithTag(106) as! UILabel" is commented out the label doesn't change to "54" but when I activate it then it works. I think it may be due to creating a copy of the value in the array, but I don't fully understand it.

I feel like there should be a more Swifty way of doing this, although the above will work by assigning each of the labels outside of the for loop I was hoping someone could help with an more elegant and brief way of creating these that will allow me to do it dynamically by just changing the number of players variable before loading the view.

Thanks!


Solution

  • Eliminate all of the discrete label properties. Just have two label arrays. Then your code becomes something like this:

    var playerReboundLabels = [UILabel]()
    var playerScoreLabels = [UILabel]()
    var numberOfPlayers = 6
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Create the rebound labels. Do something similar for the score labels
        for player in 0..<numberOfPlayers {
            let label = UILabel()
            label.backgroundColor = UIColor(red: 255/255, green: 197/255, blue: 142/255, alpha: 1.0)
            label.textAlignment = .center
            label.font = UIFont(name: "AmericanTypewriter-Bold", size: 23)
            label.text = "\(playerReboundAttemptsArray[player])"
            label.adjustsFontSizeToFitWidth = true
            label.isUserInteractionEnabled = true
            playerStatsView.addSubview(label)
            playerReboundLabels.append(label)
        }
    
        // Access a label via index, for example:
        playerReboundLabels[3].text = "54"
    }
    

    Look into using UIStackView or constraints to arrange the labels. Much better than trying to assign frames to each label.