рд╕реНрд╡рд┐рдлреНрдЯрдордзреНрдпреЗ рдореЗрдорд░реА рдХрд╛рд░реНрдб рдЧреЗрдо рд▓рд┐рд╣рд┐рдгреЗ

рд╕реНрд╡рд┐рдлреНрдЯрдордзреНрдпреЗ рдореЗрдорд░реА рдХрд╛рд░реНрдб рдЧреЗрдо рд▓рд┐рд╣рд┐рдгреЗ

рд╣рд╛ рд▓реЗрдЦ рдПрдХ рд╕рд╛рдзрд╛ рдореЗрдорд░реА рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдЧреЗрдо рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рдкреНрд░рдХреНрд░рд┐рдпреЗрдЪреЗ рд╡рд░реНрдгрди рдХрд░рддреЛ рдЬреНрдпрд╛рдЪрд╛ рдорд▓рд╛ рдЦрд░реЛрдЦрд░ рдЖрдирдВрдж рд╣реЛрддреЛ. рд╕реНрд╡рддрдГ рдЪрд╛рдВрдЧрд▓реЗ рдЕрд╕рдгреНрдпрд╛рд╡реНрдпрддрд┐рд░рд┐рдХреНрдд, рддреБрдореНрд╣реА рдЬрд╛рддрд╛рдирд╛ рд╕реНрд╡рд┐рдлреНрдЯ рдХреНрд▓рд╛рд╕реЗрд╕ рдЖрдгрд┐ рдкреНрд░реЛрдЯреЛрдХреЙрд▓рдмрджреНрджрд▓ рдереЛрдбреЗ рдЕрдзрд┐рдХ рдЬрд╛рдгреВрди рдШреНрдпрд╛рд▓. рдкрдг рдЖрдкрдг рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рдкреВрд░реНрд╡реА, рдЦреЗрд│ рд╕реНрд╡рддрдГ рд╕рдордЬреВрди рдШреЗрдК.

рдЖрдореНрд╣реА рдЖрдард╡рдг рдХрд░реВрди рджреЗрддреЛ: рд╕рд░реНрд╡ Habr рд╡рд╛рдЪрдХрд╛рдВрд╕рд╛рдареА - Habr рдкреНрд░реЛрдореЛ рдХреЛрдб рд╡рд╛рдкрд░реВрди рдХреЛрдгрддреНрдпрд╛рд╣реА рд╕реНрдХрд┐рд▓рдмреЙрдХреНрд╕ рдХреЛрд░реНрд╕рдордзреНрдпреЗ рдирд╛рд╡рдиреЛрдВрджрдгреА рдХрд░рддрд╛рдирд╛ 10 рд░реВрдмрд▓ рд╕рд╡рд▓рдд.

рд╕реНрдХрд┐рд▓рдмреЙрдХреНрд╕ рд╢рд┐рдлрд╛рд░рд╕ рдХрд░рддреЛ: рд╢реИрдХреНрд╖рдгрд┐рдХ рдСрдирд▓рд╛рдЗрди рдЕрднреНрдпрд╛рд╕рдХреНрд░рдо "рдкреНрд░реЛрдлреЗрд╢рди рдЬрд╛рд╡рд╛ рдбреЗрд╡реНрд╣рд▓рдкрд░".

рдореЗрдорд░реА рдХрд╛рд░реНрдб рдХрд╕реЗ рдЦреЗрд│рд╛рдпрдЪреЗ

рдЦреЗрд│рд╛рдЪреА рд╕реБрд░реБрд╡рд╛рдд рдкрддреНрддреНрдпрд╛рдВрдЪреНрдпрд╛ рд╕рдВрдЪрд╛рдЪреНрдпрд╛ рдкреНрд░рд╛рддреНрдпрдХреНрд╖рд┐рдХрд╛рдиреЗ рд╣реЛрддреЗ. рддреЗ рддреЛрдВрдб рдХрд░реВрди рдЭреЛрдкрддрд╛рдд (рдЕрдиреБрдХреНрд░рдореЗ, рдкреНрд░рддрд┐рдорд╛ рдЦрд╛рд▓реА). рдЬреЗрд╡реНрд╣рд╛ рддреБрдореНрд╣реА рдХреЛрдгрддреНрдпрд╛рд╣реА рдПрдХрд╛рд╡рд░ рдХреНрд▓рд┐рдХ рдХрд░рддрд╛ рддреЗрд╡реНрд╣рд╛ рдкреНрд░рддрд┐рдорд╛ рдХрд╛рд╣реА рд╕реЗрдХрдВрджрд╛рдВрд╕рд╛рдареА рдЙрдШрдбрддреЗ.

рдЦреЗрд│рд╛рдбреВрдЪреЗ рдХрд╛рд░реНрдп рд╕рдорд╛рди рдЪрд┐рддреНрд░рд╛рдВрд╕рд╣ рд╕рд░реНрд╡ рдХрд╛рд░реНрдбреЗ рд╢реЛрдзрдгреЗ рдЖрд╣реЗ. рдЬрд░, рдкрд╣рд┐рд▓реЗ рдХрд╛рд░реНрдб рдЙрдШрдбрд▓реНрдпрд╛рдирдВрддрд░, рддреБрдореНрд╣реА рджреБрд╕рд░реЗ рдХрд╛рд░реНрдб рдЙрд▓рдЯрд▓реЗ рдЖрдгрд┐ рдЪрд┐рддреНрд░реЗ рдЬреБрд│рд▓реА, рддрд░ рджреЛрдиреНрд╣реА рдХрд╛рд░реНрдбреЗ рдЙрдШрдбреА рд░рд╛рд╣рддреАрд▓. рддреЗ рдЬреБрд│рдд рдирд╕рд▓реНрдпрд╛рд╕, рдХрд╛рд░реНрдбреЗ рдкреБрдиреНрд╣рд╛ рдмрдВрдж рдХреЗрд▓реА рдЬрд╛рддрд╛рдд. рд╕рд░реНрд╡ рдХрд╛рд╣реА рдЙрдШрдбрдгреНрдпрд╛рдЪреЗ рдзреНрдпреЗрдп рдЖрд╣реЗ.

рдкреНрд░рдХрд▓реНрдк рд░рдЪрдирд╛

рдпрд╛ рдЧреЗрдордЪреА рд╕реЛрдкреА рдЖрд╡реГрддреНрддреА рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ рдЦрд╛рд▓реАрд▓ рдШрдЯрдХрд╛рдВрдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ:

  • рдПрдХ рдирд┐рдпрдВрддреНрд░рдХ: GameController.swift.
  • рдПрдХ рджреГрд╢реНрдп: CardCell.swift.
  • рджреЛрди рдореЙрдбреЗрд▓: MemoryGame.swift рдЖрдгрд┐ Card.swift.
  • рдШрдЯрдХрд╛рдВрдЪрд╛ рд╕рдВрдкреВрд░реНрдг рд╕рдВрдЪ рдЙрдкрд▓рдмреНрдз рдЕрд╕рд▓реНрдпрд╛рдЪреА рдЦрд╛рддреНрд░реА рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА Main.storyboard.

рдЖрдореНрд╣реА рдЦреЗрд│рд╛рдЪреНрдпрд╛ рд╕рд░реНрд╡рд╛рдд рд╕реЛрдкреНрдпрд╛ рдШрдЯрдХрд╛рдкрд╛рд╕реВрди рд╕реБрд░реБрд╡рд╛рдд рдХрд░рддреЛ, рдХрд╛рд░реНрдбреНрд╕.

рдХрд╛рд░реНрдб.рд╕реНрд╡рд┐рдлреНрдЯ

рдХрд╛рд░реНрдб рдореЙрдбреЗрд▓рдордзреНрдпреЗ рддреАрди рдЧреБрдгрдзрд░реНрдо рдЕрд╕рддреАрд▓: рдкреНрд░рддреНрдпреЗрдХ рдУрд│рдЦрдгреНрдпрд╛рд╕рд╛рдареА рдЖрдпрдбреА, рдХрд╛рд░реНрдбрдЪреА рд╕реНрдерд┐рддреА (рд▓рдкрд▓реЗрд▓реЗ рдХрд┐рдВрд╡рд╛ рдЙрдШрдбреЗ) рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рджрд░реНрд╢рд╡рд┐рд▓реЗрд▓реЗ рдмреБрд▓рд┐рдпрди рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓ рдЖрдгрд┐ рдХрд╛рд░реНрдбрд╡рд░реАрд▓ рдкреНрд░рддрд┐рдорд╛рдВрд╕рд╛рдареА рдЖрд░реНрдЯрд╡рд░реНрдХ URL.

class Card {        
    var id: String    
    var shown: Bool = false    
    var artworkURL: UIImage!
}

рдирдХрд╛рд╢рд╛рдВрд╕рд╣ рд╡рд╛рдкрд░рдХрд░реНрддрд╛ рдкрд░рд╕реНрдкрд░рд╕рдВрд╡рд╛рдж рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ рдпрд╛ рдкрджреНрдзрддреАрдВрдЪреА рджреЗрдЦреАрд▓ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЕрд╕реЗрд▓:

рдХрд╛рд░реНрдбрд╡рд░ рдкреНрд░рддрд┐рдорд╛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рдкрджреНрдзрдд. рдпреЗрдереЗ рдЖрдореНрд╣реА рд╕рд░реНрд╡ рдЧреБрдгрдзрд░реНрдо рдбреАрдлреЙрд▓реНрдЯрд╡рд░ рд░реАрд╕реЗрдЯ рдХрд░рддреЛ. рдЖрдпрдбреАрд╕рд╛рдареА, рдЖрдореНрд╣реА NSUUIS().uuidString рд╡рд░ рдХреЙрд▓ рдХрд░реВрди рдПрдХ рд░рдБрдбрдо рдЖрдпрдбреА рддрдпрд╛рд░ рдХрд░рддреЛ.

init(image: UIImage) {        
    self.id = NSUUID().uuidString        
    self.shown = false        
    self.artworkURL = image    
}

рдУрд│рдЦрдкрддреНрд░рд╛рдВрдЪреА рддреБрд▓рдирд╛ рдХрд░рдгреНрдпрд╛рдЪреА рдкрджреНрдзрдд.

func equals(_ card: Card) -> Bool {
    return (card.id == id)    
}

рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рд░реНрдбрд╛рдЪреА рдкреНрд░рдд рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреА рдкрджреНрдзрдд - рдПрдХрд╕рд╛рд░рдЦреНрдпрд╛ рдореЛрдареНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдиреЗ рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА. рд╣реА рдкрджреНрдзрдд рд╕рдорд╛рди рдореВрд▓реНрдпрд╛рдВрд╕рд╣ рдХрд╛рд░реНрдб рдкрд░рдд рдХрд░реЗрд▓.

func copy() -> Card {        
    return Card(card: self)    
}
 
init(card: Card) {        
    self.id = card.id        
    self.shown = card.shown        
    self.artworkURL = card.artworkURL    
}

рдЖрдгрд┐ рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛ рдХрд╛рд░реНрдбреНрд╕ рд╢рдлрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрдЦреА рдПрдХ рдкрджреНрдзрдд рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ. рдЖрдореНрд╣реА рддреЗ рдЕреЕрд░реЗ рд╡рд░реНрдЧрд╛рдЪрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░реВ.

extension Array {    
    mutating func shuffle() {        
        for _ in 0...self.count {            
            sort { (_,_) in arc4random() < arc4random() }        
        }   
    }
}

рдЖрдгрд┐ рдпреЗрдереЗ рд╕рд░реНрд╡ рдЧреБрдгрдзрд░реНрдо рдЖрдгрд┐ рдкрджреНрдзрддреАрдВрд╕рд╣ рдХрд╛рд░реНрдб рдореЙрдбреЗрд▓рд╕рд╛рдареА рдХреЛрдбрдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЖрд╣реЗ.

class Card {
    
    var id: String
    var shown: Bool = false
    var artworkURL: UIImage!
    
    static var allCards = [Card]()
 
    init(card: Card) {
        self.id = card.id
        self.shown = card.shown
        self.artworkURL = card.artworkURL
    }
    
    init(image: UIImage) {
        self.id = NSUUID().uuidString
        self.shown = false
        self.artworkURL = image
    }
    
    func equals(_ card: Card) -> Bool {
        return (card.id == id)
    }
    
    func copy() -> Card {
        return Card(card: self)
    }
}
 
extension Array {
    mutating func shuffle() {
        for _ in 0...self.count {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

рдкреБрдвреЗ рдЬрд╛.

рджреБрд╕рд░реЗ рдореЙрдбреЗрд▓ MemoryGame рдЖрд╣реЗ, рдпреЗрдереЗ рдЖрдореНрд╣реА 4*4 рдЧреНрд░рд┐рдб рд╕реЗрдЯ рдХрд░рддреЛ. рдореЙрдбреЗрд▓рдордзреНрдпреЗ рдХрд╛рд░реНрдб (рдЧреНрд░рд┐рдбрд╡рд░реАрд▓ рдХрд╛рд░реНрдбреНрд╕рдЪрд╛ рдЕреЕрд░реЗ), рдХрд╛рд░реНрдбреНрд╕ рдЖрдзреАрдЪ рдЙрдШрдбрд▓реЗрд▓реЗ рдХрд╛рд░реНрдб рджрд╛рдЦрд╡рд▓реЗрд▓реЗ рдЕреЕрд░реЗ рдЖрдгрд┐ рдЧреЗрдордЪреНрдпрд╛ рд╕реНрдерд┐рддреАрдЪрд╛ рдорд╛рдЧреЛрд╡рд╛ рдШреЗрдгреНрдпрд╛рд╕рд╛рдареА рдПрдХ рдмреБрд▓рд┐рдпрди рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓ рдкреНрд▓реЗрдЗрдВрдЧ рд╕рд╛рд░рдЦреЗ рдЧреБрдгрдзрд░реНрдо рдЕрд╕рддреАрд▓.

class MemoryGame {        
    var cards:[Card] = [Card]()    
    var cardsShown:[Card] = [Card]()    
    var isPlaying: Bool = false
}

рдЖрдореНрд╣рд╛рд▓рд╛ рдЧреНрд░реАрдбрд╕рд╣ рд╡рд╛рдкрд░рдХрд░реНрддрд╛ рдкрд░рд╕реНрдкрд░рд╕рдВрд╡рд╛рдж рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдкрджреНрдзрддреА рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рджреЗрдЦреАрд▓ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ.

рдЧреНрд░рд┐рдбрдордзреНрдпреЗ рдХрд╛рд░реНрдбреНрд╕ рд╢рдлрд▓ рдХрд░рдгрд╛рд░реА рдкрджреНрдзрдд.

func shuffleCards(cards:[Card]) -> [Card] {       
    var randomCards = cards        
    randomCards.shuffle()                
 
    return randomCards    
}

рдирд╡реАрди рдЧреЗрдо рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреА рдкрджреНрдзрдд. рдпреЗрдереЗ рдЖрдореНрд╣реА рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд▓реЗрдЖрдЙрдЯ рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ isPlaying рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓рд▓рд╛ true рдордзреНрдпреЗ рдкреНрд░рд╛рд░рдВрдн рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдкрд╣рд┐рд▓реА рдкрджреНрдзрдд рдХреЙрд▓ рдХрд░рддреЛ.

func newGame(cardsArray:[Card]) -> [Card] {       
    cards = shuffleCards(cards: cardsArray)        
    isPlaying = true            
 
    return cards    
}

рдЖрдореНрд╣рд╛рд▓рд╛ рдЦреЗрд│ рдкреБрдиреНрд╣рд╛ рд╕реБрд░реВ рдХрд░рд╛рдпрдЪрд╛ рдЕрд╕реЗрд▓ рддрд░, рдирдВрддрд░ рдЖрдореНрд╣реА isPlaying рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓ рдЕрд╕рддреНрдп рд╡рд░ рд╕реЗрдЯ рдХрд░рддреЛ рдЖрдгрд┐ рдХрд╛рд░реНрдбреНрд╕рдЪрд╛ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд▓реЗрдЖрдЙрдЯ рдХрд╛рдвреВрди рдЯрд╛рдХрддреЛ.

func restartGame() {        
    isPlaying = false                
    cards.removeAll()        
    cardsShown.removeAll()    
}

рдХреНрд▓рд┐рдХ рдХреЗрд▓реЗрд▓реНрдпрд╛ рдХрд╛рд░реНрдбрд╛рдВрдЪреА рдкрдбрддрд╛рд│рдгреА рдХрд░рдгреНрдпрд╛рдЪреА рдкрджреНрдзрдд. рддреНрдпрд╛рдЪреНрдпрд╛рдмрджреНрджрд▓ рдирдВрддрд░ рдЕрдзрд┐рдХ.

func cardAtIndex(_ index: Int) -> Card? {        
    if cards.count > index {            
        return cards[index]        
    } else {            
        return nil        
    }    
}

рдПрдХ рдкрджреНрдзрдд рдЬреА рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХрд╛рд░реНрдбрдЪреА рд╕реНрдерд┐рддреА рдкрд░рдд рдХрд░рддреЗ.

func indexForCard(_ card: Card) -> Int? {        
    for index in 0...cards.count-1 {            
        if card === cards[index] {                
            return index            
        }      
    }
        
    return nil    
}

рдорд╛рдирдХрд╛рдВрд╕рд╣ рдирд┐рд╡рдбрд▓реЗрд▓реНрдпрд╛ рдХрд╛рд░реНрдбрдЪреЗ рдЕрдиреБрдкрд╛рд▓рди рддрдкрд╛рд╕рдд рдЖрд╣реЗ.

func unmatchedCardShown() -> Bool {
    return cardsShown.count % 2 != 0
}

рд╣реА рдкрджреНрдзрдд **рдХрд╛рд░реНрдб рджрд╛рдЦрд╡рд▓реЗрд▓реНрдпрд╛** рдЕреЕрд░реЗрдордзреАрд▓ рд╢реЗрд╡рдЯрдЪрд╛ рдШрдЯрдХ рд╡рд╛рдЪрддреЗ рдЖрдгрд┐ рди рдЬреБрд│рдгрд╛рд░реЗ рдХрд╛рд░реНрдб рдкрд░рдд рдХрд░рддреЗ.

func didSelectCard(_ card: Card?) {        
    guard let card = card else { return }                
    
    if unmatchedCardShown() {            
        let unmatched = unmatchedCard()!                        
        
        if card.equals(unmatched) {          
            cardsShown.append(card)            
        } else {                
            let secondCard = cardsShown.removeLast()      
        }                    
    } else {            
        cardsShown.append(card)        
    }                
    
    if cardsShown.count == cards.count {            
        endGame()        
    }    
}

Main.storyboard рдЖрдгрд┐ GameController.swift

Main.storyboard рдЕрд╕реЗ рдХрд╛рд╣реАрддрд░реА рджрд┐рд╕рддреЗ:

рд╕реНрд╡рд┐рдлреНрдЯрдордзреНрдпреЗ рдореЗрдорд░реА рдХрд╛рд░реНрдб рдЧреЗрдо рд▓рд┐рд╣рд┐рдгреЗ

рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛, рддреБрдореНрд╣рд╛рд▓рд╛ рдЧреНрд░рд┐рдбрд╕рд╛рдареАрдЪреНрдпрд╛ рдкреНрд░рддрд┐рдорд╛рдВрд╕рд╣, рдХрдВрдЯреНрд░реЛрд▓рд░рдордзреНрдпреЗ viewDidLoad рдореНрд╣рдгреВрди рдирд╡реАрди рдЧреЗрдо рд╕реЗрдЯ рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ. рдЧреЗрдордордзреНрдпреЗ, рд╣реЗ рд╕рд░реНрд╡ 4*4 рдХрд▓реЗрдХреНрд╢рди рд╡реНрд╣реНрдпреВрджреНрд╡рд╛рд░реЗ рджрд░реНрд╢рд╡рд┐рд▓реЗ рдЬрд╛рдИрд▓. рдЖрдкрдг рдЕрджреНрдпрд╛рдк рдХрд▓реЗрдХреНрд╢рди рд╡реНрд╣реНрдпреВрд╢реА рдкрд░рд┐рдЪрд┐рдд рдирд╕рд▓реНрдпрд╛рд╕, рддреЗ рдпреЗрдереЗ рдЖрд╣реЗ рддреБрдореНрд╣рд╛рд▓рд╛ рд╣рд╡реА рдЕрд╕рд▓реЗрд▓реА рдорд╛рд╣рд┐рддреА рддреБрдореНрд╣реА рдорд┐рд│рд╡реВ рд╢рдХрддрд╛.

рдЖрдореНрд╣реА рдЧреЗрдордХрдВрдЯреНрд░реЛрд▓рд░рд▓рд╛ рдНрдкреНрд▓рд┐рдХреЗрд╢рдирдЪрд╛ рд░реВрдЯ рдХрдВрдЯреНрд░реЛрд▓рд░ рдореНрд╣рдгреВрди рдХреЙрдиреНрдлрд┐рдЧрд░ рдХрд░реВ. рдЧреЗрдордХрдВрдЯреНрд░реЛрд▓рд░рдХрдбреЗ рдПрдХ рдХрд▓реЗрдХреНрд╢рди рд╡реНрд╣реНрдпреВ рдЕрд╕реЗрд▓ рдЬреНрдпрд╛рдЪрд╛ рдЖрдореНрд╣реА IBOutlet рдореНрд╣рдгреВрди рд╕рдВрджрд░реНрдн рджреЗрдК. рджреБрд╕рд░рд╛ рд╕рдВрджрд░реНрдн IBAction onStartGame() рдмрдЯрдгрд╛рдЪрд╛ рдЖрд╣реЗ, рд╣реЗ UIButton рдЖрд╣реЗ, рддреБрдореНрд╣реА рддреЗ PLAY рдирд╛рд╡рд╛рдЪреНрдпрд╛ рд╕реНрдЯреЛрд░реАрдмреЛрд░реНрдбрдордзреНрдпреЗ рдкрд╛рд╣реВ рд╢рдХрддрд╛.

рдирд┐рдпрдВрддреНрд░рдХрд╛рдВрдЪреНрдпрд╛ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрдмрджреНрджрд▓ рдереЛрдбреЗрд╕реЗ:

  • рдкреНрд░рдердо, рдЖрдореНрд╣реА рджреЛрди рдореБрдЦреНрдп рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рд╕реБрд░реВ рдХрд░рддреЛ - рдЧреНрд░рд┐рдб: рдЧреЗрдо = рдореЗрдорд░реА рдЧреЗрдо(), рдЖрдгрд┐ рдХрд╛рд░реНрдбреНрд╕рдЪрд╛ рд╕рдВрдЪ: рдХрд╛рд░реНрдбреНрд╕ = [рдХрд╛рд░реНрдб]().
  • рдЖрдореНрд╣реА рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕ viewDidLoad рдореНрд╣рдгреВрди рд╕реЗрдЯ рдХрд░рддреЛ, рд╣реА рдкрд╣рд┐рд▓реА рдкрджреНрдзрдд рдЖрд╣реЗ рдЬреА рдЧреЗрдо рдЪрд╛рд▓реВ рдЕрд╕рддрд╛рдирд╛ рдХреЙрд▓ рдХреЗрд▓реА рдЬрд╛рддреЗ.
  • рд╕рдВрдЧреНрд░рд╣ рджреГрд╢реНрдп рд▓рдкрд╡рд┐рд▓реЗрд▓реЗ рд╡рд░ рд╕реЗрдЯ рдХреЗрд▓реЗ рдЖрд╣реЗ рдХрд╛рд░рдг рд╡рд╛рдкрд░рдХрд░реНрддрд╛ рдкреНрд▓реЗ рджрд╛рдмреЗрдкрд░реНрдпрдВрдд рд╕рд░реНрд╡ рдХрд╛рд░реНрдб рд▓рдкрд╡рд▓реЗ рдЬрд╛рддрд╛рдд.
  • рдЖрдореНрд╣реА PLAY рджрд╛рдмрддрд╛рдЪ, onStartGame IBAction рд╡рд┐рднрд╛рдЧ рд╕реБрд░реВ рд╣реЛрддреЛ рдЖрдгрд┐ рдЖрдореНрд╣реА collectionView isHidden рдкреНрд░реЙрдкрд░реНрдЯреА рдЦреЛрдЯреНрдпрд╛рд╡рд░ рд╕реЗрдЯ рдХрд░рддреЛ рдЬреЗрдгреЗрдХрд░реВрди рдХрд╛рд░реНрдб рджреГрд╢реНрдпрдорд╛рди рд╣реЛрддреАрд▓.
  • рдкреНрд░рддреНрдпреЗрдХ рд╡реЗрд│реА рд╡рд╛рдкрд░рдХрд░реНрддрд╛ рдХрд╛рд░реНрдб рдирд┐рд╡рдбрддреЛ рддреЗрд╡реНрд╣рд╛ didSelectItemAt рдкрджреНрдзрдд рдХреЙрд▓ рдХреЗрд▓реА рдЬрд╛рддреЗ. рдореБрдЦреНрдп рдЧреЗрдо рд▓реЙрдЬрд┐рдХ рд▓рд╛рдЧреВ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдореНрд╣реА didSelectCard рдореНрд╣рдгрддреЛ рддреНрдпрд╛ рдкрджреНрдзрддреАрдордзреНрдпреЗ.

рдпреЗрдереЗ рдЕрдВрддрд┐рдо рдЧреЗрдордХрдВрдЯреНрд░реЛрд▓рд░ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЖрд╣реЗ:

class GameController: UIViewController {
 
    @IBOutlet weak var collectionView: UICollectionView!
    
    let game = MemoryGame()
    var cards = [Card]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        game.delegate = self
        
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.isHidden = true
        
        APIClient.shared.getCardImages { (cardsArray, error) in
            if let _ = error {
                // show alert
            }
            
            self.cards = cardsArray!
            self.setupNewGame()
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
        if game.isPlaying {
            resetGame()
        }
    }
    
    func setupNewGame() {
        cards = game.newGame(cardsArray: self.cards)
        collectionView.reloadData()
    }
    
    func resetGame() {
        game.restartGame()
        setupNewGame()
    }
    
    @IBAction func onStartGame(_ sender: Any) {
        collectionView.isHidden = false
    }
}
 
// MARK: - CollectionView Delegate Methods
extension GameController: UICollectionViewDelegate, UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return cards.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCell
        cell.showCard(false, animted: false)
        
        guard let card = game.cardAtIndex(indexPath.item) else { return cell }
        cell.card = card
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath) as! CardCell
        
        if cell.shown { return }
        game.didSelectCard(cell.card)
        
        collectionView.deselectItem(at: indexPath, animated:true)
    }
}

рдЖрддрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреНрдпрд╛ рдкреНрд░реЛрдЯреЛрдХреЙрд▓рдмрджреНрджрд▓ рдереЛрдбреЗ рдмреЛрд▓реВрдпрд╛.

рдкреНрд░реЛрдЯреЛрдХреЙрд▓

рдкреНрд░реЛрдЯреЛрдХреЙрд▓рд╕рд╣ рдХрд╛рд░реНрдп рдХрд░рдгреЗ рд╣рд╛ рд╕реНрд╡рд┐рдлреНрдЯ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧрдЪрд╛ рдореБрдЦреНрдп рднрд╛рдЧ рдЖрд╣реЗ. рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рд╡рд░реНрдЧ, рд░рдЪрдирд╛ рдХрд┐рдВрд╡рд╛ рдЧрдгрдиреЗрд╕рд╛рдареА рдирд┐рдпрдо рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рдХреНрд╖рдорддрд╛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛рдд. рд╣реЗ рддрддреНрддреНрд╡ рддреБрдореНрд╣рд╛рд▓рд╛ рдореЙрдбреНрдпреВрд▓рд░ рдЖрдгрд┐ рдПрдХреНрд╕реНрдЯреЗрдВрд╕рд┐рдмрд▓ рдХреЛрдб рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЗ. рдЦрд░рдВ рддрд░, рд╣рд╛ рдПрдХ рдирдореБрдирд╛ рдЖрд╣реЗ рдЬреЛ рдЖрдореНрд╣реА рдЖрдзреАрдЪ GameController рдордзреНрдпреЗ collectionView рд╕рд╛рдареА рд▓рд╛рдЧреВ рдХрд░рдд рдЖрд╣реЛрдд. рдЖрддрд╛ рдЖрдкрд▓реА рд╕реНрд╡рддрдГрдЪреА рдЖрд╡реГрддреНрддреА рдмрдирд╡реВрдпрд╛. рд╡рд╛рдХреНрдпрд░рдЪрдирд╛ рдЕрд╕реЗ рджрд┐рд╕реЗрд▓:

protocol MemoryGameProtocol {
    //protocol definition goes here
}

рдЖрдореНрд╣рд╛рд▓рд╛ рдорд╛рд╣рд┐рдд рдЖрд╣реЗ рдХреА рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдЖрдореНрд╣рд╛рд▓рд╛ рд╡рд░реНрдЧ рд▓рд╛рдЧреВ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдирд┐рдпрдо рдХрд┐рдВрд╡рд╛ рд╕реВрдЪрдирд╛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЛ, рдореНрд╣рдгреВрди рддреЗ рдХрд╛рдп рдЕрд╕рд╛рд╡реЗрдд рдпрд╛рдЪрд╛ рд╡рд┐рдЪрд╛рд░ рдХрд░реВрдпрд╛. рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХреВрдг рдЪрд╛рд░рдЪреА рдЧрд░рдЬ рдЖрд╣реЗ.

  • рдЧреЗрдо рдкреНрд░рд╛рд░рдВрдн: memoryGameDidStart.
  • рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рд░реНрдбрдЪрд╛ рдЪреЗрд╣рд░рд╛ рдЦрд╛рд▓реА рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ: memoryGameShowCards.
  • рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рд░реНрдбрдЪрд╛ рдЪреЗрд╣рд░рд╛ рдЦрд╛рд▓реА рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ: memoryGameHideCards.
  • рдЧреЗрдордЪрд╛ рд╢реЗрд╡рдЯ: memoryGameDidEnd.

рд╕рд░реНрд╡ рдЪрд╛рд░ рдкрджреНрдзрддреА рдореБрдЦреНрдп рд╡рд░реНрдЧрд╛рд╕рд╛рдареА рд▓рд╛рдЧреВ рдХреЗрд▓реНрдпрд╛ рдЬрд╛рдК рд╢рдХрддрд╛рдд, рдЬреЗ рдЧреЗрдордХрдВрдЯреНрд░реЛрд▓рд░ рдЖрд╣реЗ.

рдореЗрдорд░реАрдЧреЗрдордбрд┐рдбрд╕реНрдЯрд╛рд░реНрдЯ

рдЬреЗрд╡реНрд╣рд╛ рд╣реА рдкрджреНрдзрдд рдЪрд╛рд▓рд╡рд▓реА рдЬрд╛рддреЗ, рддреЗрд╡реНрд╣рд╛ рдЧреЗрдо рд╕реБрд░реВ рдЭрд╛рд▓рд╛ рдкрд╛рд╣рд┐рдЬреЗ (рд╡рд╛рдкрд░рдХрд░реНрддрд╛ рдкреНрд▓реЗ рджрд╛рдмрддреЛ). рдпреЗрдереЗ рдЖрдореНрд╣реА рдлрдХреНрдд collectionView.reloadData() рд╡рд░ рдХреЙрд▓ рдХрд░реВрди рд╕рд╛рдордЧреНрд░реА рд░реАрд▓реЛрдб рдХрд░реВ, рдЬреЗ рдХрд╛рд░реНрдбреНрд╕ рд╢рдлрд▓ рдХрд░реЗрд▓.

func memoryGameDidStart(_ game: MemoryGame) {
    collectionView.reloadData()
}

рдореЗрдорд░реАрдЧреЗрдорд╢реЛрдХрд╛рд░реНрдбреНрд╕

рдЖрдореНрд╣реА рдпрд╛ рдкрджреНрдзрддреАрд▓рд╛ рд╕рдВрдЧреНрд░рд╣ SDViewSelectItemAt рд╡рд░реВрди рдХреЙрд▓ рдХрд░рддреЛ. рдкреНрд░рдердо рддреЗ рдирд┐рд╡рдбрд▓реЗрд▓реЗ рдХрд╛рд░реНрдб рджрд╛рдЦрд╡рддреЗ. рдирдВрддрд░ рдХрд╛рд░реНрдб рджрд╛рдЦрд╡рд▓реЗрд▓реНрдпрд╛ рдЕреЕрд░реЗрдордзреНрдпреЗ рди рдЬреБрд│рдгрд╛рд░реЗ рдХрд╛рд░реНрдб рдЖрд╣реЗ рдХрд╛ рддреЗ рддрдкрд╛рд╕рд╛ (рдХрд╛рд░реНрдбрд╛рдВрдЪреА рд╕рдВрдЦреНрдпрд╛ рд╡рд┐рд╖рдо рдЕрд╕рд▓реНрдпрд╛рд╕) рдПрдХ рдЕрд╕рд▓реНрдпрд╛рд╕, рдирд┐рд╡рдбрд▓реЗрд▓реНрдпрд╛ рдХрд╛рд░реНрдбрдЪреА рддреНрдпрд╛рдЪреНрдпрд╛рд╢реА рддреБрд▓рдирд╛ рдХреЗрд▓реА рдЬрд╛рддреЗ. рдЬрд░ рдЪрд┐рддреНрд░реЗ рд╕рд╛рд░рдЦреА рдЕрд╕рддреАрд▓, рддрд░ рджреЛрдиреНрд╣реА рдХрд╛рд░реНрдбреЗ рдХрд╛рд░реНрдбреНрд╕рдордзреНрдпреЗ рдЬреЛрдбрд▓реА рдЬрд╛рддрд╛рдд рдЖрдгрд┐ рд╕рдореЛрд░рд╛рд╕рдореЛрд░ рд░рд╛рд╣рддрд╛рдд. рднрд┐рдиреНрди рдЕрд╕рд▓реНрдпрд╛рд╕, рдХрд╛рд░реНрдб рд╕реЛрдбрд▓реЗ рдХрд╛рд░реНрдб рджрд╛рдЦрд╡рд▓реЗ рдЖрдгрд┐ рджреЛрдиреНрд╣реА рддреЛрдВрдб рдЦрд╛рд▓реА рдХреЗрд▓реЗ.

рдореЗрдорд░реАрдЧреЗрдорд╣рд╛рдЗрдбрдХрд╛рд░реНрдбреНрд╕

рдХрд╛рд░реНрдб рдЬреБрд│рдд рдирд╕рд▓реНрдпрд╛рд╕, рд╣реА рдкрджреНрдзрдд рдХреЙрд▓ рдХреЗрд▓реА рдЬрд╛рддреЗ рдЖрдгрд┐ рдХрд╛рд░реНрдб рдкреНрд░рддрд┐рдорд╛ рд▓рдкрд╡рд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд.

рджрд░реНрд╢рд╡рд┐рд▓реЗрд▓реЗ = рдЦреЛрдЯреЗ.

memoryGameDidEnd

рдЬреЗрд╡реНрд╣рд╛ рд╣реА рдкрджреНрдзрдд рдХреЙрд▓ рдХреЗрд▓реА рдЬрд╛рддреЗ, рддреЗрд╡реНрд╣рд╛ рдпрд╛рдЪрд╛ рдЕрд░реНрде рдЕрд╕рд╛ рд╣реЛрддреЛ рдХреА рд╕рд░реНрд╡ рдХрд╛рд░реНрдб рдЖрдзреАрдЪ рдЙрдШрдб рдЭрд╛рд▓реЗ рдЖрд╣реЗрдд рдЖрдгрд┐ рдХрд╛рд░реНрдб рджрд░реНрд╢рд╡рд┐рд▓реЗрд▓реНрдпрд╛ рд╕реВрдЪреАрдордзреНрдпреЗ рдЖрд╣реЗрдд: cardsShown.count = cards.count, рддреНрдпрд╛рдореБрд│реЗ рдЧреЗрдо рд╕рдВрдкрд▓рд╛ рдЖрд╣реЗ. isPlaying var рдЦреЛрдЯреНрдпрд╛рд╡рд░ рд╕реЗрдЯ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдореНрд╣реА endGame() рд▓рд╛ рдХреЙрд▓ рдХреЗрд▓реНрдпрд╛рдирдВрддрд░ рд╣реА рдкрджреНрдзрдд рд╡рд┐рд╢реЗрд╖рддрдГ рдХреЙрд▓ рдХреЗрд▓реА рдЬрд╛рддреЗ, рддреНрдпрд╛рдирдВрддрд░ рдЧреЗрдо рд╕рдорд╛рдкреНрддреАрдЪрд╛ рд╕рдВрджреЗрд╢ рджрд░реНрд╢рд╡рд┐рд▓рд╛ рдЬрд╛рддреЛ. рддрд╕реЗрдЪ alertController рдЪрд╛ рд╡рд╛рдкрд░ рдирд┐рдпрдВрддреНрд░рдХрд╛рд╕рд╛рдареА рд╕реВрдЪрдХ рдореНрд╣рдгреВрди рдХреЗрд▓рд╛ рдЬрд╛рддреЛ. viewDidDisappear рдореНрд╣рдгрддрд╛рдд рдЖрдгрд┐ рдЧреЗрдо рд░реАрд╕реЗрдЯ рдХреЗрд▓рд╛ рдЬрд╛рддреЛ.

рдЧреЗрдордХрдВрдЯреНрд░реЛрд▓рд░рдордзреНрдпреЗ рд╣реЗ рд╕рд░реНрд╡ рдХрд╕реЗ рджрд┐рд╕рддреЗ рддреЗ рдпреЗрдереЗ рдЖрд╣реЗ:

extension GameController: MemoryGameProtocol {
    func memoryGameDidStart(_ game: MemoryGame) {
        collectionView.reloadData()
    }
 
 
    func memoryGame(_ game: MemoryGame, showCards cards: [Card]) {
        for card in cards {
            guard let index = game.indexForCard(card)
                else { continue
            }        
            
            let cell = collectionView.cellForItem(
                at: IndexPath(item: index, section:0)
            ) as! CardCell
 
            cell.showCard(true, animted: true)
        }
    }
 
    func memoryGame(_ game: MemoryGame, hideCards cards: [Card]) {
        for card in cards {
            guard let index = game.indexForCard(card)
                else { continue
            }
            
            let cell = collectionView.cellForItem(
                at: IndexPath(item: index, section:0)
            ) as! CardCell
    
            cell.showCard(false, animted: true)
        }
    }
 
    func memoryGameDidEnd(_ game: MemoryGame) {
        let alertController = UIAlertController(
            title: defaultAlertTitle,
            message: defaultAlertMessage,
            preferredStyle: .alert
        )
 
        let cancelAction = UIAlertAction(
            title: "Nah", style: .cancel) {
            [weak self] (action) in
            self?.collectionView.isHidden = true
        }
 
        let playAgainAction = UIAlertAction(
            title: "Dale!", style: .default) {
            [weak self] (action) in
            self?.collectionView.isHidden = true
 
            self?.resetGame()
        }
 
        alertController.addAction(cancelAction)
        alertController.addAction(playAgainAction)
        
        self.present(alertController, animated: true) { }
    
        resetGame()
    }
}

рд╕реНрд╡рд┐рдлреНрдЯрдордзреНрдпреЗ рдореЗрдорд░реА рдХрд╛рд░реНрдб рдЧреЗрдо рд▓рд┐рд╣рд┐рдгреЗ
рд╡рд╛рд╕реНрддрд╡рд┐рдХ, рддреЗ рд╕рд░реНрд╡ рдЖрд╣реЗ. рддреБрдореНрд╣реА рдЧреЗрдордЪреА рддреБрдордЪреА рд╕реНрд╡рддрдГрдЪреА рдЖрд╡реГрддреНрддреА рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╣рд╛ рдкреНрд░рдХрд▓реНрдк рд╡рд╛рдкрд░реВ рд╢рдХрддрд╛.

рдЖрдирдВрджреА рдХреЛрдбрд┐рдВрдЧ!

рд╕реНрдХрд┐рд▓рдмреЙрдХреНрд╕ рд╢рд┐рдлрд╛рд░рд╕ рдХрд░рддреЛ:

рд╕реНрддреНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛