IOS开发基础 · SwiftUI · StanfordCS193p Lecture3-4

news2024/11/17 20:50:00

IOS开发

  • Lecture3
    • MVVM
    • Varieties of Types
      • struct & class
      • don't care - generics
      • Function
      • Closures
    • private(set)
    • for
    • 函数作为参数传给函数
    • 初始化顺序
  • Lecture4
    • 修改代码
    • View界面预览代码修改
    • 构建View-ViewMode点击事件
    • 让bool值反转
    • internal external name
    • print("\( )")
    • struct 复制=
    • struct本身不可更改
    • 让ViewModel能够通知View更改
    • MVVM下的成品代码
    • enum
      • like a struct
      • associated data
      • set the value
      • check an enum's state
      • methods and properties on an enum
      • get all the cases
    • switch
    • Optional
      • nil
      • if let safely-gotten associated value
      • ??
      • Optional chaining
    • 简化firstIndex
    • if let and分隔符`,`
    • isMatched的卡片处理
    • 课程最终代码

Lecture3

MVVM

Model-View-ViewModel

Model : UI Independent, Data + logic, “The Truth”
View : Reflects the Model, Stateless, Declared
ViewModel: Binds View to Model, Interpreter, Gatekeeper
请添加图片描述

Varieties of Types

struct & class

Both can

1.store vars
2.computed vars inline function { }
3.let vars
4.functions

func multiply(operand: Int, by: Int) -> Int{
	return operand * by
}
mutiply(oprand: 5, by: 6)

//two labels
// "_" indicates none external 
func multiply(_ operand: Int, by otherOperand: Int) -> Int{
	return operand * otherOperand
}
multiply(5, by: 6)

5.initializers

struct RoundedRactangle {
	init(cornerRadius: CGFloat){
		//initialize that rectangle with that cornerRadius
	}
	init(cornerSize: CGSize){
		//initialize this rectangle with that cornerSize
	}
}

Different

structclass
value typereference type
Copied when passed or assignedPassed around via pointers
Copy on writeAutomatically reference counted
Functional programmingObject-oriented programming
No inheritanceInheritance(single)
“Free” init initializes ALL vars“Free” init initializes NO vars
Mutability must be explicitly statedAlways mutable
Everything you’ve seen so far is a struct (except View which is a protocol)The ViewModel in MVVM is always a class(also, UIKit is class-based)

don’t care - generics

Swift is a strongly-typed language
We use a “don’t care” type (we call this feature “generics”)

example: Array

struct Array<Element>{
	...
	func append(_ element: Element) {...}
}
//Element is a "don't care" type

use like this:

var a = Array<Int>()
a.append(5)
a.append(20)

Function

(Int, Int) -> Bool
(Double) -> Void
() -> Array<String>
() -> Void
var foo: (Double) -> Void
//foo's type: function that takes a Double, returns nothing
func doSomething(what: () -> Bool)
//what's type: function, takes nothing, returns Bool
var operation: (double) ->Double

func square(operand: Double) -> Double {
	return operand * operand
}

operation = square
let result = operation(4)
//result will equal to 16

Closures

inline function

private(set)

other class and struct can look at the model, but can’t change it

for

for pairIndex in 0..<numberOfPairsOfCards {
            
}

函数作为参数传给函数

init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
    cards = Array<Card>()
    
    // add numberOfPairsOfCards x 2 cards to cards array
    for pairIndex in 0..<numberOfPairsOfCards {
        let content: CardContent = createCardContent(pairIndex)
        cards.append(Card(content: content))
        cards.append(Card(content: content))
    }
}

初始化顺序

这里都使用var xxx = xxx,在真正运行的时候并不知道谁先运行,所以这里的使用emojis[Index]将会产生问题

class EmojiMemoryGame{
    var emojis = ["🛺","🚑","🚎","🚃","🚜","🛩️","🚀","🚁"]
    
    private var model: MemoryGame<String> = MemoryGame<String>(numberOfPairsOfCards: 4) { pairIndex in
        emojis[pairIndex]
    }
}

修改为static类型

class EmojiMemoryGame{
    static var emojis = ["🛺","🚑","🚎","🚃","🚜","🛩️","🚀","🚁"]
    private var model: MemoryGame<String> = MemoryGame<String>(numberOfPairsOfCards: 4) { pairIndex in
        emojis[pairIndex]
    }
}

Lecture4

修改代码

//  View

import SwiftUI


struct ContentView: View {
    let viewModel: EmojiMemoryGame
    
    var body: some View {
        VStack{
            ScrollView{
                LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))] ) {
                    ForEach(viewModel.cards) { card in
                        CardView(card: card)
                            .aspectRatio(2/3, contentMode: .fit)
                            .onTapGesture {
                                viewModel.choose(card)
                            }
                    }
                }
            }.foregroundColor(.pink)
        }
        .padding(.horizontal)
    }
}

struct CardView: View{
    let card: MemoryGame<String>.Card
    
    var body: some View{
        ZStack{
            let shape = RoundedRectangle(cornerRadius: 20)
            if card.isFaceUp{
                shape.fill().foregroundColor(.white)
                shape.strokeBorder(lineWidth:10)
                Text(card.content).font(.largeTitle)
            }else{
                shape.fill()
            }
        }
    }
}
//  ViewModel
// ViewModel制定了String类
import SwiftUI

class EmojiMemoryGame{
    static let emojis = ["🛺","🚑","🚎","🚃","🚜","🛩️","🚀","🚁","⚽️","🏀","🏈","⚾️","🥎","🎾","🏉","🎱","🛼","🥊","🍎","🍇","🍐","🍌","🍋","🍊","🍉","🍓","🫐","🍒","🍈","🍑","🥦","🍍","🥥","🥝","🍆","🥑"]
    
    static func createMemoryGame() -> MemoryGame<String> {
        MemoryGame<String>(numberOfPairsOfCards: 3) { pairIndex in
            emojis[pairIndex]
        }
    }
    
    private var model: MemoryGame<String> = createMemoryGame()
    
    var cards: Array<MemoryGame<String>.Card> {
        return model.cards
    }
    
    //MARK: - Intent(s)
    func choose(_ card:MemoryGame<String>.Card){
        model.choose(card)
    }
}
//  Model
// Model中提供一个Card类,但不指定类型
import Foundation

struct MemoryGame<CardContent> {
    private(set) var cards: Array<Card>
    
    func choose(_ card: Card){
        print("hello")
    }
    
    init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
        cards = Array<Card>()
        
        // add numberOfPairsOfCards x 2 cards to cards array
        for pairIndex in 0..<numberOfPairsOfCards {
            let content: CardContent = createCardContent(pairIndex)
            cards.append(Card(content: content,id: pairIndex * 2))
            cards.append(Card(content: content,id: pairIndex * 2 + 1))
        }
    }
    
    struct Card: Identifiable{
        var isFaceUp: Bool = false
        var isMatched: Bool = false
        var content: CardContent
        var id: Int
    }
}
//main
//主程序创建一个ViewModel和一个View
import SwiftUI

@main
struct Memorize2App: App {
    let game = EmojiMemoryGame()
    
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: game)
        }
    }
}

View界面预览代码修改

因为ContentView的修改,需要提供ViewModel,故在预览界面也需要更改

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let game = EmojiMemoryGame()
        ContentView(viewModel: game)
            .preferredColorScheme(.light)
            .previewDevice("iPhone 11 Pro")
        ContentView(viewModel: game)
            .previewInterfaceOrientation(.landscapeRight)
            .preferredColorScheme(.light)
            .previewDevice("iPhone 11 Pro")
        ContentView(viewModel: game)
            .preferredColorScheme(.dark)
            .previewDevice("iPhone 11 Pro")
    }
}

构建View-ViewMode点击事件

// View
.onTapGesture {
	viewModel.choose(card)
}
// ViewModel
//MARK: - Intent(s)
func choose(_ card:MemoryGame<String>.Card){
    model.choose(card)
}
func choose(_ card: Card){
    print("hello")
}

让bool值反转

card.isFaceUp.toggle()

internal external name

let chosenIndex = index(of: card)

//first is external name
//second is internal name
//third is struct
func index(of card: Card) -> Int {
    for index in 0..<cards.count {
        if cards[index].id == card.id {
            return index
        }
    }
    return 0
}

print(“( )”)

print("chosenCard = \(chosenCard)")

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

print会将可能的一切转换为String打印出来,只要加上\( )
请添加图片描述

struct 复制=

struct在复制的时候是完整的复制,既复制后新的与原来的无关了就

struct本身不可更改

请添加图片描述
我们加上mutating

//this function can change this struct
mutating func choose(_ card: Card) {
    let chosenIndex = index(of: card)
    cards[chosenIndex].isFaceUp.toggle()
    print("\(cards)")
}

请添加图片描述
此时数据已经发生改变了,但UI不会变化
请添加图片描述

让ViewModel能够通知View更改

类加上ObservableObject,同时在需要通知的地方加上objectWillChange.send()

import SwiftUI

class EmojiMemoryGame: ObservableObject{
    static let emojis = ["🛺","🚑","🚎","🚃","🚜","🛩️","🚀","🚁","⚽️","🏀","🏈","⚾️","🥎","🎾","🏉","🥝","🍆","🥑"]
    
    static func createMemoryGame() -> MemoryGame<String> {
        MemoryGame<String>(numberOfPairsOfCards: 4) { pairIndex in
            emojis[pairIndex]
        }
    }
    
    private var model: MemoryGame<String> = createMemoryGame()
    
    var cards: Array<MemoryGame<String>.Card> {
        return model.cards
    }
    
    //MARK: - Intent(s)
    func choose(_ card:MemoryGame<String>.Card){
        objectWillChange.send()
        model.choose(card)
    }
}

或者是将,需要更改后就要通知的变量加上@Published

import SwiftUI

class EmojiMemoryGame: ObservableObject{
    static let emojis = ["🛺","🚑","🚎","🚃","🚜","🛩️","🚀","🚁","⚽️","🏀","🏈","⚾️","🥎","🎾","🏉","🎱","🛼","🥊","🍎","🍇","🍐","🍌","🍋","🍊","🍉","🍓","🫐","🍒","🍈","🍑","🥦","🍍","🥥","🥝","🍆","🥑"]
    
    static func createMemoryGame() -> MemoryGame<String> {
        MemoryGame<String>(numberOfPairsOfCards: 4) { pairIndex in
            emojis[pairIndex]
        }
    }
    
    @Published private var model: MemoryGame<String> = createMemoryGame()
    
    var cards: Array<MemoryGame<String>.Card> {
        return model.cards
    }
    
    //MARK: - Intent(s)
    func choose(_ card:MemoryGame<String>.Card){
        model.choose(card)
    }
}

此时让View观察着ViewModel,增添上@ObservedObject

import SwiftUI

struct ContentView: View {
    @ObservedObject var viewModel: EmojiMemoryGame
    
    var body: some View {
        VStack{
            ScrollView{
                LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))] ) {
                    ForEach(viewModel.cards) { card in
                        CardView(card: card)
                            .aspectRatio(2/3, contentMode: .fit)
                            .onTapGesture {
                                viewModel.choose(card)
                            }
                    }
                }
            }.foregroundColor(.pink)
        }
        .padding(.horizontal)
    }
}

MVVM下的成品代码

//
//  Memorize2App.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//
import SwiftUI

@main
struct Memorize2App: App {
    let game = EmojiMemoryGame()
    
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: game)
        }
    }
}
//
//  ContentView.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//  View

import SwiftUI


struct ContentView: View {
    @ObservedObject var viewModel: EmojiMemoryGame
    
    var body: some View {
        VStack{
            ScrollView{
                LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))] ) {
                    ForEach(viewModel.cards) { card in
                        CardView(card: card)
                            .aspectRatio(2/3, contentMode: .fit)
                            .onTapGesture {
                                viewModel.choose(card)
                            }
                    }
                }
            }.foregroundColor(.pink)
        }
        .padding(.horizontal)
    }
}

struct CardView: View{
    let card: MemoryGame<String>.Card
    
    var body: some View{
        ZStack{
            let shape = RoundedRectangle(cornerRadius: 20)
            if card.isFaceUp{
                shape.fill().foregroundColor(.white)
                shape.strokeBorder(lineWidth:10)
                Text(card.content).font(.largeTitle)
            }else{
                shape.fill()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let game = EmojiMemoryGame()
        ContentView(viewModel: game)
            .preferredColorScheme(.light)
            .previewDevice("iPhone 11 Pro")
        ContentView(viewModel: game)
            .previewInterfaceOrientation(.landscapeRight)
            .preferredColorScheme(.light)
            .previewDevice("iPhone 11 Pro")
        ContentView(viewModel: game)
            .preferredColorScheme(.dark)
            .previewDevice("iPhone 11 Pro")
    }
}
//
//  EmojiMemoryGame.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//  ViewModel
import SwiftUI

class EmojiMemoryGame: ObservableObject{
    static let emojis = ["🛺","🚑","🚎","🚃","🚜","🛩️","🚀","🚁","⚽️","🏀","🏈","⚾️","🥎","🎾","🏉","🎱","🛼","🥊","🍎","🍇","🍐","🍌","🍋","🍊","🍉","🍓","🫐","🍒","🍈","🍑","🥦","🍍","🥥","🥝","🍆","🥑"]
    
    static func createMemoryGame() -> MemoryGame<String> {
        MemoryGame<String>(numberOfPairsOfCards: 4) { pairIndex in
            emojis[pairIndex]
        }
    }
    
    @Published private var model: MemoryGame<String> = createMemoryGame()
    
    var cards: Array<MemoryGame<String>.Card> {
        return model.cards
    }
    
    //MARK: - Intent(s)
    func choose(_ card:MemoryGame<String>.Card){
        model.choose(card)
    }
}
//
//  MemoryGame.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//  Model
import Foundation

struct MemoryGame<CardContent> {
    private(set) var cards: Array<Card>
    
    mutating func choose(_ card: Card) {
        let chosenIndex = index(of: card)
        cards[chosenIndex].isFaceUp.toggle()
    }
    
    func index(of card: Card) -> Int {
        for index in 0..<cards.count {
            if cards[index].id == card.id {
                return index
            }
        }
        return 0
    }
    
    init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
        cards = Array<Card>()
        
        // add numberOfPairsOfCards x 2 cards to cards array
        for pairIndex in 0..<numberOfPairsOfCards {
            let content: CardContent = createCardContent(pairIndex)
            cards.append(Card(content: content,id: pairIndex * 2))
            cards.append(Card(content: content,id: pairIndex * 2 + 1))
        }
    }
    
    struct Card: Identifiable{
        var isFaceUp: Bool = true
        var isMatched: Bool = false
        var content: CardContent
        var id: Int
    }
}

enum

like a struct

a value type, it is copied as it is passed around

enum FastFoodMenuItem{
	case hambuger //state
	case fries
	case drink
	case cookie
}

associated data

each state can have its own “associated data”

enum FastFoodMenuItem{
	case hambuger(numberOfPatties: Int)
	case fries(size: FryOrderSize)
	case drink(String, ounces: Int)
	case cookie
}
// so FryOrderSize would be an enum too
enum FryOderSize{
	case large
	case small
}

set the value

let menuItem: FastFoodMenuItem = FastFoodMenuItem.hamburger(patties: 2)
var otherItem: FastFoodMenuItem = FastFoodMenuItem.cookie

//或者简写
let menuItem = FastFoodMenuItem.hamburger(patties: 2)
var otherItem: FastFoodMenuItem = .cookie

check an enum’s state

var menuItem = FastFoodMenuItem.hambuger(patties: 2)
switch menuItem{
	case FastFoodMenuItem.hamburger: print("burger")
	case FastFoodMenuItem.fries: print("fries")
	case FastFoodMenuItem.drink: print("drink")
	case FastFoodMenuItem.cookie: print("cookie")
}
// 简化为
switch menuItem{
	case .hamburger: print("burger")
	case .fries: print("fries")
	case .drink: break
	case .cookie: print("cookie")
	default: print("other")
}

Associated data is accessed through a switch statement using this let syntax

var menuItem = FastFoodMenuItem.drink("Coke", ounces: 32)
switch menuItem {
	case hamburger(let pattyCount): print("a burger with \(pattyCount) patties!")
	case .fries(let size): print("a \(size) order of fries")
	case .drink(let brand, let ounces): print("a \(ounces)oz \(brand)")
	case .cookie: print("a cookie!")
}

methods and properties on an enum

an enum can have methods(and computed properties) but no stored properties
Notice: the use of _ if we don’t care about that piece of associated data

enum FastFoodMenuItem {
	case hamburger(numberOfPatties: Int)
	case fries(size: FryOrderSize)
	case drink(String, ounces: Int)
	case cookie

	func isInludedInSpecialOrder(number: Int) -> Bool { 
		switch self {
			case .hamburger(let pattyCount): return pattyCount == number
			case .fries, .cookie: return true// a drink and cookie in every special order
			case .drink(_, let ounces): return ounces == 16//&16oz drink of any kind
		}
	}
	var calories: Int{ }
}

get all the cases

enum TeslaModel: CaseIterable {
	case X
	case S
	case Three
	case Y
}
//Now this enum will have a static var allCases that you can iterate over
for model in TeslaModel.allCases {
	reportSaledNumbers(for: model)
}
func reportSalesNumbers(for model:TeslaModel) {
	switch model {...}
}

switch

let s: String = "hello"
switch s {
	case "goodbye": ...
	case "hello": ...
	default: ...
}

multiple lines allowed

var menuItem = FastFoodMenuItem.fries(sizes: FryOrderSize.large)
switch menuItem{
	case .hamburger: print("burger")
	case .fries: 
		print("yummy")
		print("fries")
	case .drink: 
		print("drink")
	case .cookie: print("cookie")
}

Optional

It’s just an enum

enum Optional<T>{
	case none
	case some(T)
}

we have a value that can sometimes be “not set” or “unspecified” or “undertermined”

nil

var hello: String?            var hello: Optional<String> = .none
var hello: String? = "hello"  var hello: Optional<String> = .some("hello")
var hello: String? = nil.     var hello: Optional<String> = .none

You can then assign it the value nil (Optional.none)
Or you can assign it something of the type T

can access the associated value either by force(with !)

enum Optional<T> {
	case none
	case some(T)
}

let hello: String? = ...
print(hello!)

switch hello {
	case .none: //raise an exception(crash)
	case .some(let data): print(data)
}

if let safely-gotten associated value

if let safehello = hello {
	print(safehello)
} else {
	//do something else
}
// 就是下面这段程序的含义
switch hello {
	case .none: { //do something else }
	case .some(let data): print(data)
}

??

enum Optional<T> {
	case none
	case some(T)
}

let x: String? = ...
let y = x ?? "foo"
// 就是下面这段程序的含义
switch x {
	case .none: y = "foo"
	case .some(let data): y = data
}

Optional chaining

enum Optional<T> {
	case none
	case some(T)
}

let x: String? = ...
let y = x?.foo()?.bar?.z

switch x {
	case .none: y = nil
	case .some(let xval)):
		switch xval.foo() {
			case .none: y = nil
			case .some(let xfooval):
				switch xfooval.bar {
					case .none: y = nil
					case .some(let xfbval): y = xfbval.z
				}
		}
}

简化firstIndex

mutating func choose(_ card: Card) {
    if let chosenIndex = cards.firstIndex(where: {$0.id == card.id}) {
        cards[chosenIndex].isFaceUp.toggle()
    }
}

if let and分隔符

if let chosenIndex = cards.firstIndex(where: { $0.id == card.id}) , !cards[chosenIndex].isFaceUp{ //如果不是nil才执行

isMatched的卡片处理

在这里插入图片描述

课程最终代码

//  Memorize2App.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//  Main
import SwiftUI

@main
struct Memorize2App: App {
    let game = EmojiMemoryGame()
    
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: game)
        }
    }
}
//  ContentView.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//  View
import SwiftUI


struct ContentView: View {
    @ObservedObject var viewModel: EmojiMemoryGame
    
    var body: some View {
        VStack{
            ScrollView{
                LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))] ) {
                    ForEach(viewModel.cards) { card in
                        CardView(card: card)
                            .aspectRatio(2/3, contentMode: .fit)
                            .onTapGesture {
                                viewModel.choose(card)
                            }
                    }
                }
            }.foregroundColor(.pink)
        }
        .padding(.horizontal)
    }
}

struct CardView: View{
    let card: MemoryGame<String>.Card
    
    var body: some View{
        ZStack{
            let shape = RoundedRectangle(cornerRadius: 20)
            if card.isFaceUp{
                shape.fill().foregroundColor(.white)
                shape.strokeBorder(lineWidth:10)
                Text(card.content).font(.largeTitle)
            } else if card.isMatched{
                shape.opacity(0) // 不透明度设置为0,相当于完全透明
            } else {
                shape.fill()
            }
        }
    }
}
//  EmojiMemoryGame.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//  ViewModel
import SwiftUI

class EmojiMemoryGame: ObservableObject{
    static let emojis = ["🛺","🚑","🚎","🚃","🚜","🛩️","🚀","🚁","⚽️","🏀","🏈","⚾️","🥎","🎾","🏉","🎱","🛼","🥊"]
    
    static func createMemoryGame() -> MemoryGame<String> {
        MemoryGame<String>(numberOfPairsOfCards: 8) { pairIndex in
            emojis[pairIndex]
        }
    }
    
    @Published private var model: MemoryGame<String> = createMemoryGame()
    
    var cards: Array<MemoryGame<String>.Card> {
        return model.cards
    }
    
    //MARK: - Intent(s)
    func choose(_ card:MemoryGame<String>.Card){
        model.choose(card)
    }
}
//  MemoryGame.swift
//  Memorize2
//
//  Created by zhj12399 on 2023/1/6.
//  Model
import Foundation

struct MemoryGame<CardContent> where CardContent: Equatable{
    private(set) var cards: Array<Card>
    
    private var indexOfTheOneAndOnlyFaceUpCard: Int?
    
    mutating func choose(_ card: Card) {
        if let chosenIndex = cards.firstIndex(where: { $0.id == card.id}),
           !cards[chosenIndex].isFaceUp,
           !cards[chosenIndex].isMatched
        { //如果不是nil才执行
            if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard { //如果indexOfTheOneAndOnlyFaceUpCard为nil则去下面的else
                if cards[chosenIndex].content == cards[potentialMatchIndex].content {
                    cards[chosenIndex].isMatched = true
                    cards[potentialMatchIndex].isMatched = true
                }
                indexOfTheOneAndOnlyFaceUpCard = nil
            } else { //此处指之前已经选了两个卡值已经被设置为nil,或一张卡都没选
                for index in cards.indices {
                    cards[index].isFaceUp = false
                }
                indexOfTheOneAndOnlyFaceUpCard = chosenIndex
            }
            //执行完逻辑后在把选的卡翻面
            cards[chosenIndex].isFaceUp.toggle()
        }
    }
    
    init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
        cards = Array<Card>()
        
        // add numberOfPairsOfCards x 2 cards to cards array
        for pairIndex in 0..<numberOfPairsOfCards {
            let content: CardContent = createCardContent(pairIndex)
            cards.append(Card(content: content,id: pairIndex * 2))
            cards.append(Card(content: content,id: pairIndex * 2 + 1))
        }
    }
    
    struct Card: Identifiable{
        var isFaceUp: Bool = false
        var isMatched: Bool = false
        var content: CardContent
        var id: Int
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160282.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NeurIPS 2022: S3GC 可扩展图聚类

talk 文章的创新性不大&#xff0c;meta-review是给了 如果还可接收&#xff0c;再考虑。 但是 本文确实是 可扩展图聚类的重要一步。已有的方法 或多或少只在 小的数据集上 进行聚类。 存在一些非聚类的通用gnn方法进行采样。本文就是利用采样降低复杂度&#xff0c;并进行大…

基于FPGA的UDP 通信(四)

引言 前文链接&#xff1a; 基于FPGA的UDP 通信&#xff08;一&#xff09; 基于FPGA的UDP 通信&#xff08;二&#xff09; 基于FPGA的UDP 通信&#xff08;三&#xff09; 本文基于FPGA与MATLAB作千兆以太网通信模块UDP数据接收实验板级验证&#xff1a;FPGA接收上位机数…

大前端 TOB 0.5 WordPress模板 漂亮大气自适应多终端多功能

tob主题基于WordPress程序&#xff0c;响应式布局支持电脑、平板和手机的完美展示。tob适用于各种图片展示网站、新闻站、电影站、美图站、资源站等等&#xff0c;扁平化设计、公众号展示、打赏功能、列表无限加载、相册功能。tob是基于WordPress程序的主题&#xff0c;由theme…

多线程与高并发(五)

【ReentrantLock源码】&#xff1a; 【AQS源码】&#xff1a; 【公平与非公平】&#xff1a; 【公平】&#xff1a; 线程想要获得一把锁&#xff0c;乖乖的去这把锁的等待队列里排队————公平。 【非公平】&#xff1a; 线程想要获得一把锁&#xff0c;不去排队&#xff0c…

嵌入式实时操作系统的设计与开发(九)

同步机制 aCoral信号量机制不仅可以实现临界资源互斥访问&#xff0c;控制系统中临界资源多个实例的使用&#xff0c;还可以用于维护线程之间、线程和中断之间的同步。 当信号量用来实现同步时&#xff0c;起始值为0&#xff0c;如一个线程正在等待某个I/O操作&#xff0c;当…

【外贸小知识】通过whatsapp获取流量的几种小方法

相信做外贸的小伙伴们对于是whatsap比较熟悉的&#xff0c;都想通过whatsapp来获取更多流量&#xff0c;更多用户。今天我们花漾灵动小编就给大家汇总了通过whatsapp获取流量的几种小方法&#xff0c;希望能对新手小白有点作用哦&#xff01;通过whatsapp获取流量的几种小方法1…

【PyTorch深度学习实践】07_Dataset和Dataloader

文章目录1. Epoch&#xff0c;Iteration&#xff0c;Batch-Size2. Dataset 和 Dataloader2.1 Dataset2.2 Dataloader2.2.1 例子2.2.2 enumerate函数3. 完整代码1. Epoch&#xff0c;Iteration&#xff0c;Batch-Size 参考博客 2. Dataset 和 Dataloader 参考博客 功能概览 2…

2023年浙江建筑八大员(标准员)精选真题题库及答案

百分百题库提供建筑八大员&#xff08;标准员&#xff09;考试试题、建筑八大员&#xff08;标准员&#xff09;考试真题、建筑八大员&#xff08;标准员&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 14.根据《施工现场临…

Electron Vue之间的通讯 自定义标题栏实现最小化全屏关闭功能

方便以后定制化使用&#xff0c;学习记录一下。 话不多说&#xff0c;先看看效果吧。 效果 版本 electron ^13.0.0 知识点 Vue 相互通讯 Electron 标题栏主要逻辑代码 新建public\preload.js文件&#xff0c;用于前端全局发送和监听消息。 const { contextBridge, ipcRen…

【Linux】Linux权限的理解

文章目录&#x1f3aa; Linux权限的理解&#x1f680;1.shell命令及其运行原理&#x1f680;2.Linux权限概念⭐2.1 用户与root身份切换⭐2.2 用户与用户身份切换⭐2.3 单条指令提权&#x1f680;3.Linux文件权限⭐3.1 文件属性(第一个字符)⭐3.2 文件角色划分与文件属性⭐3.3 文…

线性代数第四章 向量组的线性相关性

向量组及其线性组合一.向量、向量组1.向量n个有次序的数a1,a2,...,an所组成的数组称为n维向量&#xff0c;这n个数称为该向量的n个分量&#xff0c;第i个数ai称为第i个分量n维向量可以写成一行&#xff0c;也可以写成一列&#xff0c;在没有指明是行向量还是列向量时&#xff0…

Authing 入选长城战略咨询《2022中国潜在独角兽企业研究报告》

12 月 23 日&#xff0c;长城战略咨询&#xff08;GEI&#xff09;发布《2022 中国潜在独角兽企业研究报告》&#xff08;下称《报告》&#xff09;。作为身份云行业领先的代表企业&#xff0c; Authing 凭借着过硬的技术实力和突出的创新能力&#xff0c;首次入选中国潜在独角…

软件测试工程师为什么要写测试用例?

软件测试工程师为什么要写测试用例&#xff1f;相信从事软件测试行业的从业者来讲&#xff0c;测试用例并不陌生。因为测试用例不仅仅是一组简单的文档&#xff0c;它包含前提条件、输入、执行条件和预期结果等等重要内容&#xff0c;并且能够完成一定的测试目的和需求。下面本…

深度学习(20)—— ConvNext 使用

深度学习&#xff08;20&#xff09;—— ConvNext 使用 本篇主要使用convnext做分类任务&#xff0c;其中使用convnext-tiny&#xff0c;其主要有5块 stage0stage1stage2stage3head 文章目录深度学习&#xff08;20&#xff09;—— ConvNext 使用Part 1 ModelPart 2 Traini…

【数据结构】一篇博客带你实现双向带头循环链表!!!(零基础小白也可以看懂)

目录 0.前言 1. 简述双向带头链表 2.双向带头循环链表的实现 2.1 设计双向带头循环链表结构体 2.2双向带头循环链表的初始化 2.3双向带头循环链表的尾插 2.4双向带头循环链表的尾删 2.5双向带头循环链表的头插 2.6双向带头循环链表的头删 2.7双向带头循环链表的插入 …

【面试题】notify() 和 notifyAll()方法的使用和区别

【面试题】notify() 和 notifyAll()方法的使用和区别 Java中notify和notifyAll的区别 何时在Java中使用notify和notifyAll&#xff1f; 【问】为什么wait()一定要放在循环中&#xff1f; Java中通知和notifyAll方法的示例 Java中通知和notify方法的示例 Java中notify和no…

22年我在CSDN做到了名利兼收

写在前面 hi朋友&#xff0c;我是几何心凉&#xff0c;感谢你能够点开这篇文章&#xff0c;看到这里我觉得我们是有缘分的&#xff0c;因着这份缘分&#xff0c;我希望你能够看完我的分享&#xff0c;因为下面的分享就是要汇报给你听的&#xff0c;这篇文章是在 2022 年 12 月 …

从0到1完成一个Vue后台管理项目(二十三、初代项目完成、已开源)

开源地址 项目地址 项目还在优化&#xff0c;会增加很多新功能&#xff0c;UI也会重新设计&#xff0c;已经在修改啦&#xff01; 最近打算加一些组件、顺便分享一些好用的开源项目 现在正在做迁移到vue3TS的版本、预计年后会完事&#xff0c;然后迁移到vite、遇到的问题和报…

docker安装prometheus和grafana

docker安装prometheus和grafana docker安装prometheus和grafana 概念简述安装prometheus 第一步&#xff1a;确保安装有docker第二步&#xff1a;拉取镜像第三步&#xff1a;准备相关挂载目录及文件第四步&#xff1a;启动容器第五步&#xff1a;访问测试 安装grafana 第一步&…

分享66个ASP源码,总有一款适合您

ASP源码 分享66个ASP源码&#xff0c;总有一款适合您 66个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1Jf78pfAPaFo6QhHWWHEq0A?pwdwvtg 提取码&#xff1a;wvtg 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&…