使用二维数组创建UIButton网格遇索引越界错误求助
Hey there! Let's work through your problems step by step—first fixing that index out-of-range error, then cleaning up your offset logic for a more elegant implementation.
1. Fixing the "Index out of range" Error
The crash happens because your btnArr is an empty 2D array when you try to assign buttons to btnArr[xPos][yPos]. You need to pre-allocate the array's structure first, so those indices exist before you use them.
Looking at your loops:
xruns from 1 to 9 → 9 rows (sincex < 10)yruns from 5 to 13 → 9 columns (sincey < 14)
We can initialize the array with this fixed size first. Here's the updated buildBoard function:
func buildBoard(){ let btnSize = 40 // Define the number of rows and columns based on your original loops let totalRows = 9 let totalCols = 9 // Initialize the 2D array with empty UIButtons to reserve space btnArr = Array(repeating: Array(repeating: UIButton(), count: totalCols), count: totalRows) var xPos = 0 for x in 1..<10 { var yPos = 0 for y in 5..<14 { let button = UIButton(frame: CGRect(x: x*btnSize, y: y*btnSize, width: btnSize, height: btnSize)) button.layer.cornerRadius = 0.5 * button.bounds.size.width button.clipsToBounds = true button.backgroundColor = .lightGray button.setTitle("\(xPos), \(yPos)", for: .normal) button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside) self.view.addSubview(button) // Now the array has valid indices, so assignment works btnArr[xPos][yPos] = button yPos += 1 } xPos += 1 } }
2. Refining the Offset Logic (Make It More Elegant)
Your current approach uses hardcoded x=1 and y=5 to center the grid, but we can calculate the offset dynamically to keep the grid centered on any screen size. This also lets you ditch the separate x/xPos variables and use the array indices directly:
func buildBoard(){ let btnSize = 40 let totalRows = 9 let totalCols = 9 // Calculate total grid size let gridTotalWidth = totalCols * btnSize let gridTotalHeight = totalRows * btnSize // Calculate offset to center the grid on the screen let horizontalOffset = (view.bounds.width - CGFloat(gridTotalWidth)) / 2 let verticalOffset = (view.bounds.height - CGFloat(gridTotalHeight)) / 2 // Initialize the 2D array btnArr = Array(repeating: Array(repeating: UIButton(), count: totalCols), count: totalRows) // Loop directly using array indices (xPos = row, yPos = column) for xPos in 0..<totalRows { for yPos in 0..<totalCols { let buttonX = horizontalOffset + CGFloat(xPos) * CGFloat(btnSize) let buttonY = verticalOffset + CGFloat(yPos) * CGFloat(btnSize) let button = UIButton(frame: CGRect(x: buttonX, y: buttonY, width: btnSize, height: btnSize)) button.layer.cornerRadius = 0.5 * button.bounds.size.width button.clipsToBounds = true button.backgroundColor = .lightGray button.setTitle("\(xPos), \(yPos)", for: .normal) button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside) self.view.addSubview(button) btnArr[xPos][yPos] = button } } }
3. Can This 2D Array Approach Achieve Your Offset Goal?
Absolutely! The 2D array is perfect for this use case—it lets you easily access any button by its grid position (btnArr[row][col]) regardless of the visual offset. Whether you use hardcoded starting positions or dynamic centering, the array will always map cleanly to your button grid.
By the way, your homemade game project sounds really cool! If you run into more issues (like handling button states, game logic, or auto-layout adjustments), feel free to share more details—I’d be happy to help out with your project.
内容的提问来源于stack exchange,提问作者SwiftHobby

