下面的代码:
VStack (spacing: 0) {
ForEach (0..<myMaze.height, id: \.self) { (y: Int) in
HStack (spacing: 0) {
ForEach (0..<myMaze.width, id: \.self) { (x: Int) in
DrawRoom(xPos: x, yPos: y)
} // ForEach x
} // HSTack
} // ForEach y
} // VSTack
和
struct DrawRoom: View {
@ObservedObject var myMaze = Maze.sharedInstance
var xPos: Int
var yPos: Int
var body: some View {
Square(value: myMaze.value)
.stroke(Color.gray, style: StrokeStyle(lineWidth: 3))
.frame(width: myMaze.sideLength, height: myMaze.sideLength)
}
init(xPos: Int, yPos: Int) {
self.xPos = xPos
self.yPos = yPos
myMaze.value = myMaze.mazeData[xPos][yPos]
print("DrawRoom: [\(xPos), \(yPos)]")
}
}
...在控制台中显示以下输出:
X, Y
DrawRoom: [0, 2]
DrawRoom: [1, 2]
DrawRoom: [2, 2]
DrawRoom: [0, 0]
DrawRoom: [1, 0]
DrawRoom: [2, 0]
DrawRoom: [0, 1]
DrawRoom: [1, 1]
DrawRoom: [2, 1]
令人惊讶的是,“Y”从2开始,然后是0,最后是1,但它应该如下所示:
X, Y
DrawRoom: [0, 0]
DrawRoom: [1, 0]
DrawRoom: [2, 0]
DrawRoom: [0, 1]
DrawRoom: [1, 1]
DrawRoom: [2, 1]
DrawRoom: [0, 2]
DrawRoom: [1, 2]
DrawRoom: [2, 2]
有人能告诉我为什么吗?我的代码有问题吗?
作为参考,“Square”根据myMaze的值绘制线条。mazeData[xPos][yPos]
下面是整个代码:
//
// Maze.swift
// NewMaze
//
// Created by Philippe Lagarrigue on 26/12/2020.
//
import Foundation
import SwiftUI
let side = ["North", "East", "South", "West"]
let direction = [1, 2, 4, 8]
let wall = (North: 1, East: 2, South: 4, West: 8) // 15 means all walls
let exit = (toNorth: 16, toEast: 32, toSouth: 64, toWest: 128) // 0 means no exit
struct Room {
var x: Int
var y: Int
var roomsToExit: Int
}
class Maze: ObservableObject {
// Properties
static let sharedInstance = Maze()
var viewWidth = CGFloat(0.0) //{ didSet { update(flag: 2) } }
var viewHeight = CGFloat(0.0) //{ didSet { update(flag: 2) } }
var ready2Draw = false
var width = 0 // Number of rooms (horizontally)
var height = 0 // Number of rooms (vertically)
@Published var widthDouble = CGFloat(3.0) { didSet { ready2Draw = false } }
@Published var heightDouble = CGFloat(3.0) { didSet { ready2Draw = false } }
var oneWay = true
var exitRoom = Room(x: 0, y: 0, roomsToExit: 0)
var farestRoom = Room(x: 0, y: 0, roomsToExit: 0)
var currentPos = Room(x: 0, y: 0, roomsToExit: 0)
var mazeData = [[Int]]()
//var width: Int { return Int(widthDouble) } // Number of rooms (horizontally)
//var height: Int { return Int(heightDouble) } // Number of rooms (vertically)
var numberOfRooms: Int { return self.width * self.height }
var sideLength = CGFloat(40.0) //{ didSet { print("sideLength: \(sideLength)") } }
var value = 15 // { didSet { print("value: \(value)") } }
func defineExit() {
let choosenSide = Int.random(in: 0...3) // N, E, S or W
// Fill array with 15 which means that all walls are set
mazeData = Array(repeating: Array(repeating: 15, count: self.height), count: self.width)
//var count = 0
//for x in 0..<width {
// for y in 0..<height {
// print(x, y, count, count/height, count%height)
// mazeData[x][y] = 15
// count += 1
// }
//}
print("\nchoosenSide: \(side[choosenSide]) (\(choosenSide))")
if (choosenSide + 1) % 2 == 0 {
print("Axe: E-W")
exitRoom.x = choosenSide == 3 ? 0 : self.width - 1
exitRoom.y = Int.random(in: 1..<self.height)
} else {
print("Axe: N-S")
exitRoom.x = Int.random(in: 1..<self.width)
exitRoom.y = choosenSide == 2 ? self.height - 1 : 0
}
currentPos.x = exitRoom.x
currentPos.y = exitRoom.y
currentPos.roomsToExit = 1
//print("The exit room is at \(exitRoom.x), \(exitRoom.y)")
mazeData[currentPos.x][currentPos.y] = 15 - direction[choosenSide]
print("The exit room is at [\(exitRoom.x), \(exitRoom.y)] => \(mazeData[currentPos.x][currentPos.y])")
}
init() {
//buildMaze()
}
func update(flag: Int) {
if flag & 1 == 1 {
width = Int(widthDouble)
height = Int(heightDouble)
}
if flag & 2 == 2 {
let viewW = viewWidth/widthDouble
let viewH = viewHeight/heightDouble
let side = (viewW < viewH ? viewW : viewH) - 1.0
self.sideLength = side - side * 0.1
print("Maze init:", width, "x", height, "=", numberOfRooms)
}
ready2Draw = false
}
func dummy() {
let wd = widthDouble
let hd = heightDouble
widthDouble = 3
heightDouble = 3
update(flag: 3)
defineExit()
widthDouble = wd
heightDouble = hd
}
func buildMaze(){
dummy() // This is the only way I found to get the maze displayed correctly
update(flag: 3)
defineExit()
//var rooms = numberOfRooms
//while (rooms > 0) {
// newRoom()
// rooms -= 1
//}
ready2Draw = true
}
func newRoom() {
}
}
//
// ContentView.swift
// NewMaze
//
// Created by Philippe Lagarrigue on 26/12/2020.
//
import SwiftUI
import Combine // << needed for Just publisher below
struct ContentView: View {
@ObservedObject var myMaze: Maze = .init()
@State private var selectedView = 0
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
TabView(selection: $selectedView) {
NavigationView() .tabItem { Image("navigation"); Text("Navigation") }.tag(0)
.onReceive(timer) { input in selectedView = 1; self.timer.upstream.connect().cancel() }
SettingsView() .tabItem { Image("settings"); Text("Settings") }.tag(1)
MapView() .tabItem { Image("map"); Text("Map") }.tag(2)
}
//.onReceive(Just(selectedView)) { print($0) }
//.onLongPressGesture { myMaze.ready2Draw = true }
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct NavigationView : View {
@ObservedObject var myMaze = Maze.sharedInstance
var body : some View {
GeometryReader { geometry in
Path { path in
myMaze.viewWidth = geometry.size.width
myMaze.viewHeight = geometry.size.width
}
}
}
}
struct Square: Shape {
//@ObservedObject var myMaze = Maze.sharedInstance
var value: Int
func path(in rect: CGRect) -> Path {
var path = Path()
if (value & wall.North == wall.North) {
path.move(to: CGPoint(x: rect.minX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
}
if (value & wall.East == wall.East) {
path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
}
if (value & wall.South == wall.South) {
path.move(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
}
if (value & wall.West == wall.West) {
path.move(to: CGPoint(x: rect.maxX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY))
}
return path
}
init(value: Int) {
self.value = value
//if value != 15 {
// print("Square: \(value)")
//}
}
}
struct DrawRoom: View {
@ObservedObject var myMaze = Maze.sharedInstance
var xPos: Int
var yPos: Int
let numbers = false
var body: some View {
if numbers {
Text("\(myMaze.value, specifier: "%.02d")")
.font(.system(size: 18, design: .monospaced))
.foregroundColor(myMaze.value == 15 ? Color.black : Color.red)
} else {
Square(value: myMaze.value)
.stroke(Color.gray, style: StrokeStyle(lineWidth: 3))
.frame(width: myMaze.sideLength, height: myMaze.sideLength)
}
}
init(xPos: Int, yPos: Int) {
self.xPos = xPos
self.yPos = yPos
myMaze.value = myMaze.mazeData[xPos][yPos]
print("DrawRoom: [\(xPos), \(yPos)]")
}
}
struct MapView : View {
@ObservedObject var myMaze = Maze.sharedInstance
var body : some View {
if myMaze.ready2Draw {
VStack (spacing: 0) {
ForEach (0..<myMaze.height, id: \.self) { (y: Int) in
HStack (spacing: 0) {
ForEach (0..<myMaze.width, id: \.self) { (x: Int) in
DrawRoom(xPos: x, yPos: y)
} // ForEach x
} // HSTack
} // ForEach y
} // VSTack
} else {
Button(action: { action() }) { Text("Build maze") }
.padding()
.background(Color.red)
.cornerRadius(20)
.foregroundColor(.white)
.font(.title)
.shadow(color: .gray, radius: 20.0, x: 10, y: 10)
}
} // body
func action() {
myMaze.buildMaze()
}
}
struct SettingsView : View {
@ObservedObject var myMaze = Maze.sharedInstance
@State private var isEditing1 = false
@State private var isEditing2 = false
var body : some View {
HStack(alignment: VerticalAlignment.top) {
VStack(alignment: .leading) {
Text("Settings\n") .font(.title)
Text("Number of rooms (horizontally): \(myMaze.widthDouble, specifier: "%.0f")")
.foregroundColor(isEditing1 ? .red : .black)
Slider(value: $myMaze.widthDouble, in: 3...32, step: 1,
onEditingChanged: { editing in isEditing1 = editing })
Text("\nNumber of rooms (vertically): \(myMaze.heightDouble, specifier: "%.0f")")
.foregroundColor(isEditing2 ? .red : .black)
Slider(value: $myMaze.heightDouble, in: 3...32, step: 1,
onEditingChanged: { editing in isEditing2 = editing })
Text("\nIn a maze with only one way, you can find the exit by touching the wall or hedge with the hand nearest to it, left or right. Keep that same hand touching the wall and keep walking. This may take you on a horribly long route, but it will get you out.")
Toggle("One way", isOn: $myMaze.oneWay)
Spacer()
Button(action: { action() }) { Text("Build maze") }
.padding()
.background(Color.red)
.cornerRadius(20)
.foregroundColor(.white)
.font(.title)
.shadow(color: .gray, radius: 20.0, x: 10, y: 10)
Spacer()
} .padding()
}
}
func action() {
myMaze.buildMaze()
}
}
1条答案
按热度按时间juud5qan1#
您的控制台输出不是您所期望的原因是由于VStack和HStack容器中循环的顺序。
在你的VStack中,你有一个从0到myMaze.height - 1的y循环。在这个循环中,你有一个HStack,它包含一个从0到myMaze.width - 1的x循环。
这意味着HStack循环(for x)将在VStack循环(for y)的每次迭代中执行,这将为每个y值创建一行DrawRoom视图。但是,由于您使用myMaze.mazeData[xPos][yPos]访问mazeData数组,因此x值的变化速度比y值快,这导致行以意外的顺序打印。
为了解决这个问题,你应该切换循环的顺序,这样HStack循环(针对x)首先对每个y值执行,然后再移动到下一行:
通过这个更改,DrawRoom视图将被创建并逐行打印,从上到下,从左到右,这应该会产生您所期望的控制台输出。