Skip to content

Instantly share code, notes, and snippets.

@petercv petercv/GameView.swift Secret
Created Aug 15, 2019

Embed
What would you like to do?
class AssetLoader {
init(pack: String? = nil) {
// ...
}
func load(_ name: String) -> UIImage {
return UIImage(systemName: "smiley")!
}
}
struct AssetLoaderEnvironmentKey: EnvironmentKey {
static var defaultValue: AssetLoader = AssetLoader()
}
extension EnvironmentValues {
var assetLoader: AssetLoader {
get { self[AssetLoaderEnvironmentKey.self] }
set { self[AssetLoaderEnvironmentKey.self] = newValue}
}
}
enum Screen {
case home
case level(level: Int)
}
class GameState: ObservableObject {
@Published var screen: Screen = .home
@Published var score: Int = 0
@Published var highScore: Int = UserDefaults.standard.integer(forKey: "highScore")
var level: Int {
switch screen {
case .level(let level):
return level
default:
return 1
}
}
}
struct GameProgressHiddenPreferenceKey: PreferenceKey {
typealias Value = Bool
static var defaultValue: Value = false
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue() || value
}
}
extension View {
func gameProgressHidden(_ value: Bool) -> some View {
preference(key: GameProgressHiddenPreferenceKey.self, value: value)
}
}
struct ScreenLink<Content>: View where Content: View {
@EnvironmentObject private var state: GameState
let screen: Screen
let content: Content
init(_ screen: Screen, @ViewBuilder content: () -> Content) {
self.screen = screen
self.content = content()
}
func go() {
withAnimation {
self.state.screen = self.screen
}
}
var body: some View {
Button(action: self.go) {
content
}
}
}
struct LevelView: View {
let level: Int
@EnvironmentObject private var state: GameState
@Environment(\.assetLoader) private var assetLoader
func quit() {
state.screen = .home
}
var body: some View {
VStack {
Text("Level \(level)")
Image(uiImage: assetLoader.load("level_\(level)"))
Button(action: self.quit) {
Text("Quit")
}
}
.gameProgressHidden(false)
}
}
struct HomeView: View {
@EnvironmentObject private var state: GameState
var body: some View {
VStack {
Text("Highscore: \(state.highScore)")
ScreenLink(.level(level: 1)) {
Text("Start")
}
}
.gameProgressHidden(true)
}
}
struct GameProgressView: View {
@EnvironmentObject private var state: GameState
var body: some View {
VStack {
Divider()
HStack {
Text("Level: \(state.level)")
Spacer()
Text("Score: \(state.score)")
}
.padding()
}
}
}
struct GameView: View {
@ObservedObject var state = GameState()
@State var showProgress = false
let assetLoader = AssetLoader(pack: UserDefaults.standard.object(forKey: "assetPack") as? String)
func viewForScreen(_ screen: Screen) -> some View {
switch screen {
case .home:
return AnyView(HomeView())
case .level(let level):
return AnyView(LevelView(level: level))
}
}
var body: some View {
VStack {
viewForScreen(state.screen)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: Alignment.center)
if showProgress {
GameProgressView()
}
}
.environment(\.assetLoader, assetLoader)
.environmentObject(state)
.onPreferenceChange(GameProgressHiddenPreferenceKey.self) { value in
self.showProgress = !value
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.