笔者之前观看的是2017版斯坦福swift教程,今天来更新2020版最新的教程
第二课——讲述使用swiftUI,使用MVVM模式进行构建
下面上代码,代码中有注释,当然也可以留言交流
//
// ContentView.swift
// Memorize
//
// Created by 王@@ on 2021/2/6.
//
//MVVM中的view
import SwiftUI
struct ContentView: View {
//这里定义viewModel去访问viewModel
var viewModel: EmojiMemoryGame
var body: some View {
VStack{
HStack {
ForEach(viewModel.cards) { card in
//此处可以对卡牌的选择意图进行反馈
CardView(card: card).onTapGesture{
self.viewModel.choose(card: card)
}
}
}}
.foregroundColor(.orange)
.padding()
.font(.largeTitle)
// .overlay(Circle().stroke(Color.white, lineWidth: 3))
.shadow(radius: 5)
}
}
struct CardView: View {
var card: MemoryGame<String>.Card
var body: some View {
ZStack() {
if card.isFaceUp{
RoundedRectangle(cornerRadius:10.00).fill(Color.white)
RoundedRectangle(cornerRadius: 10.00).stroke(lineWidth: 3)
Text(card.concent)
}else{
RoundedRectangle(cornerRadius:10.00).fill()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(viewModel: EmojiMemoryGame())
}
}
//
// MemoryGame.swift
// Memorize
//
// Created by 王@@ on 2021/2/13.
//
//MVVM中的model
import Foundation
struct MemoryGame<CardConcent> {
var cards: Array<Card>
func choose(card: Card){
print("card chosen: \(card)")
}
//接收到卡牌对的传参之后进行一个初始化,并且将回调一个方法型的参数,这个方法型的参数传一个int类型的数字,告诉viewmodel,是哪组卡牌对需要进行初始化,这时viewmodel只需要返回一个CardConcent给model进行初始化即可
init(numberOfPairsOfCards: Int, cardConteneFactory: (Int) -> CardConcent) {
//创建一个空的纸牌阵列
cards = Array<Card>()
//遍历卡牌对,创建卡牌队列
for pairIndex in 0..<numberOfPairsOfCards{
let content = cardConteneFactory(pairIndex)
cards.append(Card( concent: content, id: pairIndex*2))
cards.append(Card( concent: content, id: pairIndex*2+1))
}
}
//Identifiable是可迭代事物协议,添加这个协议swift才可以识别卡牌并且迭代
struct Card: Identifiable {
//三个变量,是否正面朝上、是否被匹配、内容(这里使用MemoryGame的通用类型CardConcent,因为与ui分离,这里可以放入任何想放入的东西)
var isFaceUp: Bool = true
var isMatched: Bool = false
var concent: CardConcent
var id: Int
}
}
``
```swift
//
// EmojiMemoryGame.swift
// Memorize
//
//
//MVVM中的view model,实现用户的意图,向view展示model
import SwiftUI
//func creatCardContent(pairIndex: Int) -> String{
// return "😀"
//}
//(pairIndex: Int) -> String in "😀" 等价于 pairIndex in "😀"
class EmojiMemoryGame{
//model是一个称呼,model确定model- MemoryGame的使用类型,可以通过定义一个自己的cards 去返回已经被private保护的model的cards
//这里给model一个卡牌对的传参,并且接收model的调用去返回一个卡牌内容cardcontent
//numberOfPairsOfCards: 2, cardConteneFactory: { pairIndex in "😀"}) 等价于 (numberOfPairsOfCards: 2){ pairIndex in "😀"}
private var model: MemoryGame<String> = EmojiMemoryGame.createMemoryGame()
static func createMemoryGame() -> MemoryGame<String>{
let emojis = ["👻","🎃","🕷"]
return MemoryGame<String>(numberOfPairsOfCards: emojis.count){ pairIndex in
return emojis[pairIndex]
}
}
//MARK: - Access to the model(对model的一些访问)
var cards: Array<MemoryGame<String>.Card>{
model.cards
}
//MARK: - Intent(s)一些意图
func choose(card: MemoryGame<String>.Card){
//请model来选择卡牌
model.choose(card: card)
}
}
//
// SceneDelegate.swift
// Memorize
//
// Created by 王@@ on 2021/2/6.
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let game = EmojiMemoryGame()
let contentView = ContentView(viewModel: game)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}