Nick Harris

Swift – UIKit Collections and Map

leave a comment »

For background please see this post about Euchre

Euchre is a four player game which means that my UI is basically the same 7 elements for each player but displayed at different positions on the view. I use basic UIKit objects for these. There are the 5 UIImageView’s that represent the 5 cards in a users hand along with a single UIImageView representing the card played as well as a UILabel for any user messages like “I deal!” or “I call Hearts!”.

Here’s what the Storyboard looks. (I’m not a designer so feel free to laugh at it. As I wrote before, I use Euchre to learn and make a few bucks off ads along the way)

Screen Shot 2015 09 14 at 12 57 51 AM

I realized a few versions ago that having helper collections for these UIKit objects makes updating the UI during game play simpler. When my game play view controller loads I create these helper collections:

@IBOutlet weak var leftCard1, leftCard2, leftCard3, leftCard4, leftCard5: UIImageView!

@IBOutlet weak var partnerCard1, partnerCard2, partnerCard3, partnerCard4, partnerCard5: UIImageView!

@IBOutlet weak var rightCard1, rightCard2, rightCard3, rightCard4, rightCard5: UIImageView!

@IBOutlet weak var userCard1, userCard2, userCard3, userCard4, userCard5: UIImageView!

@IBOutlet weak var leftCardPlayed, partnerCardPlayed, rightCardPlayed, userCardPlayed: UIImageView!

@IBOutlet weak var leftPlayerLabel, partnerPlayerLabel, rightPlayerLabel, userPlayerLabel: UILabel!

 

var leftCards: [UIImageView] = []

var partnerCards: [UIImageView] = []

var rightCards: [UIImageView] = []

var userCards: [UIImageView] = []

 

var allPlayedCards: [EuchrePlayer.PlayerPosition: UIImageView] = [:]

var allPlayerHandCards: [EuchrePlayer.PlayerPosition: [UIImageView]] = [:]

var allPlayerLabels: [EuchrePlayer.PlayerPosition: UILabel] = [:]

 

internal func createHelperCollections() {


    leftCards = [leftCard1, leftCard2, leftCard3, leftCard4, leftCard5]

    partnerCards = [partnerCard1, partnerCard2, partnerCard3, partnerCard4, partnerCard5]

    rightCards = [rightCard1, rightCard2, rightCard3, rightCard4, rightCard5]

    userCards = [userCard1, userCard2, userCard3, userCard4, userCard5]

        

    allPlayerHandCards = [

        EuchrePlayer.PlayerPosition.LeftPlayer: leftCards,

        EuchrePlayer.PlayerPosition.PartnerPlayer: partnerCards,

        EuchrePlayer.PlayerPosition.RightPlayer: rightCards,

        EuchrePlayer.PlayerPosition.UserPlayer: userCards

    ]

        

    allPlayedCards = [

        EuchrePlayer.PlayerPosition.LeftPlayer: leftCardPlayed,

        EuchrePlayer.PlayerPosition.PartnerPlayer: partnerCardPlayed,

        EuchrePlayer.PlayerPosition.RightPlayer: rightCardPlayed,

        EuchrePlayer.PlayerPosition.UserPlayer: userCardPlayed

    ]

       

    allPlayerLabels = [

        EuchrePlayer.PlayerPosition.LeftPlayer: leftPlayerLabel,

        EuchrePlayer.PlayerPosition.PartnerPlayer: partnerPlayerLabel,

        EuchrePlayer.PlayerPosition.RightPlayer: rightPlayerLabel,

        EuchrePlayer.PlayerPosition.UserPlayer: userPlayerLabel

    ]

}

 

These help me get to specific UI assets when I need to change them but during the course of a Euchre game its fairly common to hide groups of UI assets from the view. Originally I started out with functions like this:

internal func hideAllPlayerCards() {

        

    for playerCards in allPlayerHandCards.values {

        for playerCard in playerCards {

            playerCard.hidden = true

        }

    }

}

    

internal func hideAllPlayedCards() {

    for playedCard in allPlayedCards.values {

        playedCard.hidden = true

    }

}

    

internal func hideAllPlayerLabels() {

    for playedCard in allPlayerLabels.values {

        playedCard.hidden = true

    }

}

 

This works great… but…

One of the comments about Swift that’s stuck with me is my friend Nate saying he’d close any PR with a for loop (in jest of course). But now every time I write a Swift for loop I wonder if there’s a better way.

In my case I have a collection and I want to alter them all in the same way. After a bit of googling I came across this post about Swift’s hidden “each” method.

With a little casting I re-wrote my code as:

internal func hideAllPlayerCards() {

    for playerHandCards in allPlayerHandCards.values {

        _ = playerHandCards.map { imageView in imageView.hidden = true }

    }

}

    

internal func hideAllPlayedCards() {

    _ = [UIImageView](allPlayedCards.values).map { imageView in imageView.hidden = true }

}

    

internal func hideAllPlayerLabels() {

    _ = [UILabel](allPlayerLabels.values).map { label in label.hidden = true }

}

 

This looks a lot different! I’m not a fan of using _ = for map but its the only way I’ve found to get rid of the warnings about unused results. Ugly, but once I had this written I realized I could do something like this:

let hideView = {

    (view: UIView) -> () in

    view.hidden = true

}

 

internal func hideAllPlayerCards() {

    for playerHandCards in allPlayerHandCards.values {

        _ = playerHandCards.map(hideView)

    }

}

    

internal func hideAllPlayerLabels() {

    _ = [UILabel](allPlayerLabels.values).map(hideView)

}

    

internal func hideAllPlayedCards() {

    _ = [UIImageView](allPlayedCards.values).map(hideView)

}

 

That’s pretty slick! I was originally uncomfortable with with lack of verbosity in the code but once the idea of map sunk in it was a clear winner.

I bet there’s a way to get rid of the for loop with a dictionary of arrays as well but I didn’t find it tonight.

Thoughts?

Advertisements

Written by Nick Harris

September 14, 2015 at 7:32 am

Posted in Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: