WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

SOLVED: (Thank you gays!)Why the preview of my program displays different from the one displayed in the simulator?

Forums > SwiftUI

Why the preview of my program displays different from the one displayed in the simulator?

Forgive me about my english, because I am not a native english language speaker.

Things are like this, I wrote a program when I was following CS193p. Everything seemed OK in the preview, but when I pressed the button "Command + R" to run it in a simulator. Maybe there are some "Static Misused" in my code, so I posted all of my swift files. I really want to know the reason for the error, thank you for your answer.

This is the main file.

//
//  assignment_2App.swift
//  assignment_2
//
//  Created by Tao Feng on 2022/4/16.
//

import SwiftUI

@main
struct assignment_2App: App {
    var body: some Scene {
        let game = EmojiMemoryGame(theme1: "Vehicle", pairsShowed: 4)  

        // The previews actually show 4 pairs of cards which is what I need it to be

        WindowGroup {
            ContentView(viewModel: game)
        }
    }
}

This is previews part of the contentView file

//
//  ContentView.swift
//  assignment_2
//
//  Created by Tao Feng on 2022/4/16.
//

import SwiftUI

struct ContentView: View {
    @ObservedObject var viewModel: EmojiMemoryGame

    var body: some View {
        VStack {
            Text("Memorized!")
                .font(.largeTitle)
                .padding(.bottom)
            HStack {
                Text("Theme: \(viewModel.themeShow.name)")
                    .foregroundColor(viewModel.themeShow.color)
                Spacer()
                Text("Scores: \(viewModel.scoreShow)")
            }.padding()
            ScrollView {
                LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
                    ForEach(viewModel.cards) {
                        card in CardView(card: card).aspectRatio(2/3, contentMode: .fit)
                            .onTapGesture {
                                viewModel.choose(card)
                            }
                    }
                }
            }
            .foregroundColor(viewModel.themeShow.color)
            .padding(.horizontal)
            Button {
                viewModel.newGame()
            } label: {
                Text("New Game")
            }
        }

    }

}

struct CardView: View {
    var card: MemoryGame<String>.Card

    var body: some View {
        ZStack {
            let shape = RoundedRectangle(cornerRadius: 30)
            if card.isFaceUp {
                shape.fill(.white)
                shape.strokeBorder(lineWidth: 5)
                Text(card.content).font(.largeTitle)
            } else if card.isMatched {
                shape.opacity(0)
            }
            else {
                shape.fill()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let game = EmojiMemoryGame(theme1: "Vehicle", pairsShowed: 4)

                // The previews actually show 4 pairs of cards which is what I need it to be

        ContentView(viewModel: game)
    }
}

This is the viewModel of my program

//
//  EmojiMemoryGame.swift
//  assignment_2
//
//  Created by Tao Feng on 2022/4/16.
//

import Foundation

class EmojiMemoryGame: ObservableObject {

    static var themes: Array<Theme> = [
        Theme(
            name: "Vehicle",
            emojis: ["πŸš—", "πŸš•", "πŸš™", "🚌", "🚎",
                     "🏎", "πŸš“", "πŸš‘", "πŸš’", "🚐",
                     "πŸ›»", "🚚", "πŸš›", "🚜", "🦯",
                     "🦽", "🦼", "🩼", "πŸ›΄", "🚲"],
            color: .blue,
            pairs: 20

            // The simulator actually shows 20 pairs of cards which I felt confused

        ),
        Theme(
            name: "Fruit",
            emojis: ["🍏", "🍎", "🍐", "🍊", "πŸ‹",
                     "🍌", "πŸ‰", "πŸ‡", "πŸ“", "🫐",
                     "🍈", "πŸ’", "πŸ‘" ,"πŸ₯­", "🍍"],
            color: .green,
            pairs: 15
        ),
        Theme(
            name: "Sport",
            emojis: ["⚽️", "πŸ€", "🏈", "⚾️", "πŸ₯Ž",
                     "🎾", "🏐", "πŸ‰", "πŸ₯", "🎱",
                     "πŸͺ€", "πŸ“", "🏸", "πŸ’", "πŸ‘",
                     "πŸ₯", "🏏", "πŸͺƒ"],
            color: .orange,
            pairs: 18
        )
    ]

    static var themePick: Theme = themes[0]

    init(theme1: String, pairsShowed: Int) {
        for index in EmojiMemoryGame.themes.indices {
            if theme1 == EmojiMemoryGame.themes[index].name {
                EmojiMemoryGame.themePick = EmojiMemoryGame.themes[index]
                EmojiMemoryGame.themePick.pairs = pairsShowed
            }
        }
    }

    static func startGame(theme: Theme) ->  MemoryGame<String> {
        MemoryGame<String>(pairsOfCards: theme.pairs) {
            index in theme.emojis[index]
        }
    }

    var themeShow: Theme {
        return EmojiMemoryGame.themePick
    }

    @Published private var model: MemoryGame<String> = startGame(theme: themePick)

    var cards: Array<MemoryGame<String>.Card> {
        return model.cards
    }

    var scoreShow: Int {
        return model.scores
    }

    // MARK: - Intent(s)
    func choose(_ card: MemoryGame<String>.Card) {
//        card.isFaceUp.toggle()
//        objectWillChange.send()
        model.choose(card)
    }

    func newGame() {
        EmojiMemoryGame.themePick = EmojiMemoryGame.themes.randomElement()!
        EmojiMemoryGame.themePick.emojis.shuffle()
        model = EmojiMemoryGame.startGame(theme: EmojiMemoryGame.themePick)

    }
}

And this is the Model part of my program

//
//  MemoryGame.swift
//  assignment_2
//
//  Created by Tao Feng on 2022/4/16.
//

import Foundation

struct MemoryGame<CardContent> where CardContent: Equatable{
    var cards: Array<Card>

    private(set) var scores: Int = 0

    private var indexOfTheOneAndOnlyOne: Int?

    mutating func choose(_ card: Card) {

        if let chosenIndex = index(of: card), !cards[chosenIndex].isFaceUp, !cards[chosenIndex].isMatched {
            if let indexOfExistingOne = indexOfTheOneAndOnlyOne {

                if cards[indexOfExistingOne].content == cards[chosenIndex].content {
                    cards[indexOfExistingOne].isMatched = true
                    cards[chosenIndex].isMatched = true
                    scores += 2
                }
                else {
                    if cards[chosenIndex].hasBeenShowed {
                        scores -= 1
                    }
                    if cards[indexOfExistingOne].hasBeenShowed {
                        scores -= 1
                    }
                    cards[chosenIndex].hasBeenShowed = true
                    cards[indexOfExistingOne].hasBeenShowed = true
                }
                indexOfTheOneAndOnlyOne = nil
            }
            else {
                for index in cards.indices {
                    cards[index].isFaceUp = false
                }
                indexOfTheOneAndOnlyOne = chosenIndex
            }
            cards[chosenIndex].isFaceUp.toggle()

        }
        print("\(cards)")
    }

    func index(of card: Card) -> Int? {
        for i in 0..<cards.count {
            if card.id == cards[i].id {
                return i
            }
        }
        return nil
    }

    struct Card: Identifiable {
        let id: Int
        let content: CardContent
        var isFaceUp: Bool = false
        var isMatched: Bool = false
        var hasBeenShowed = false
    }

    init(pairsOfCards: Int, contentGenerator: (Int) -> CardContent) {
        cards = Array<Card>()

        for index in 0..<pairsOfCards {
            let contents = contentGenerator(index)

            cards.append(Card(id: index*2, content: contents))
            cards.append(Card(id: index*2+1, content: contents))
        }
        cards.shuffle()
    }
}

And this is the Theme Model

//
//  ThemePick.swift
//  assignment_2
//
//  Created by Tao Feng on 2022/4/16.
//

import Foundation
import SwiftUI

struct Theme {
    var name: String
    var emojis: Array<String>
    var color: Color
    var pairs: Int

    init(name: String, emojis: Array<String>, color: Color, pairs: Int) {
        self.name = name
        self.emojis = emojis
        self.color = color
        self.pairs = pairs
    }
}

   

While the code is helpful, more helpful would be screenshots showing what your app looks like in the simulator vs the preview.

1      

The LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) will change the card layout depend on the number of columns that it can fill across the screen so this will change from device to device.

   

But LazyVGrid will only use as many items as it's given; it won't create items. So that wouldn't explain why 40 cards are shown when only 8 are expected.

The problem is that EmojiMemoryGame.model is being initialized using the static function startGame, which defaults to using 20 items since it runs before the class' init does. Then, in the init, you don't assign anything different to model, so it keeps using the initial 20 items.

If you change this line:

@Published private var model: MemoryGame<String> = startGame(theme: themePick)

to this:

@Published private var model: MemoryGame<String>

And then add this line in your init:

model = EmojiMemoryGame.startGame(theme: EmojiMemoryGame.themePick)

it should work.

1      

Sorry @roosterboy is correct I miss read the problem.

   

Hacking with Swift is sponsored by Fernando Olivares

SPONSORED Fernando's book will guide you in fixing bugs in three real, open-source, downloadable apps from the App Store. Learn applied programming fundamentals by refactoring real code from published apps. Hacking with Swift readers get a $10 discount!

Read the book

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.