SwiftUI中实现按钮矩阵点击切换填充颜色的问题
SwiftUI 齿科图表组件修复:实现五按钮矩阵及颜色切换
问题描述
需要在SwiftUI中构建包含顶部、右侧、左侧、底部、中心五个按钮的矩阵结构,点击任意按钮可切换其填充颜色。当前编写的代码(含OdontogramComponentView、View扩展及EdgeBorder结构体)未达到预期效果,具体信息如下:
预期效果

现有代码
OdontogramComponentView 主视图
import SwiftUI struct OdontogramComponentView: View { @State private var leftButtonColor = Color.blue @State private var leftBorderColor = Color.black @State private var rightButtonColor = Color.red @State private var rightBorderColor = Color.black @State private var leftButtonPressed = false @State private var rightButtonPressed = false var body: some View { ZStack { HStack(spacing: 0) { Button(action: { self.leftButtonPressed = true self.rightButtonPressed = false self.changeLeftButtonColor() }) { Text("") .foregroundColor(.white) .frame(width: 40, height: 80) .background(leftButtonColor) .border(width: 2, edges: [.top, .leading, .bottom], color: leftBorderColor) } Button(action: { self.leftButtonPressed = false self.rightButtonPressed = true self.changeRightButtonColor() }) { Text("") .foregroundColor(.white) .frame(width: 40, height: 80) .background(rightButtonColor) .border(width: 2, edges: [.top, .trailing, .bottom], color: rightBorderColor) } } Rectangle() .fill(Color.black) .frame(width: 110, height: 2) .offset(x: 0, y: 0) .rotationEffect(.degrees(45)) Rectangle() .fill(Color.black) .frame(width: 2, height: 110) .offset(x: 0, y: 0) .rotationEffect(.degrees(45)) Button(action: { self.leftButtonPressed = false self.rightButtonPressed = false self.changeLeftButtonColor() self.changeRightButtonColor() }) { Image(systemName: "") .foregroundColor(.white) .font(.system(size: 20, weight: .bold)) .frame(width: 40, height: 40) .background(leftButtonColor) .border(leftBorderColor, width: 2) } } .frame(width: 120, height: 120) } private func changeLeftButtonColor() { if leftButtonPressed { switch leftButtonColor { case .blue: leftButtonColor = .red leftBorderColor = .black case .red: leftButtonColor = .white leftBorderColor = .black case .white: leftButtonColor = .blue leftBorderColor = .black default: break } } } private func changeRightButtonColor() { if rightButtonPressed { switch rightButtonColor { case .blue: rightButtonColor = .red rightBorderColor = .black case .red: rightButtonColor = .white rightBorderColor = .black case .white: rightButtonColor = .blue rightBorderColor = .black default: break } } } }
View 扩展代码
import SwiftUI extension View { func border(width: CGFloat, edges: [Edge], color: Color) -> some View { overlay(EdgeBorder(width: width, edges: edges).foregroundColor(color)) } }
EdgeBorder 结构体代码
import SwiftUI struct EdgeBorder: Shape { var width: CGFloat var edges: [Edge] func path(in rect: CGRect) -> Path { edges.map { edge -> Path in switch edge { case .top: return Path(.init(x: rect.minX, y: rect.minY, width: rect.width, height: width)) case .bottom: return Path(.init(x: rect.minX, y: rect.maxY - width, width: rect.width, height: width)) case .leading: return Path(.init(x: rect.minX, y: rect.minY, width: width, height: rect.height)) case .trailing: return Path(.init(x: rect.maxX - width, y: rect.minY, width: width, height: rect.height)) } }.reduce(into: Path()) { $0.addPath($1) } } }
当前效果

修复方案
问题分析
- 原代码仅实现左右两个按钮,缺少顶部、底部按钮的布局与逻辑
- 中心按钮背景色错误绑定到
leftButtonColor,未独立管理状态 - 颜色切换逻辑依赖
pressed标记,点击其他按钮会重置状态导致切换失效 - 旋转矩形分割线与预期的十字分割不符
完整修复代码
1. 重构状态与布局
用枚举统一颜色切换逻辑,为五个按钮创建独立状态,重构布局匹配预期结构:
import SwiftUI // 统一管理按钮颜色状态 enum ButtonColorState: CaseIterable { case blue, red, white var color: Color { switch self { case .blue: return .blue case .red: return .red case .white: return .white } } // 切换到下一个颜色状态 func next() -> ButtonColorState { let allCases = ButtonColorState.allCases guard let currentIndex = allCases.firstIndex(of: self) else { return self } let nextIndex = (currentIndex + 1) % allCases.count return allCases[nextIndex] } } struct OdontogramComponentView: View { // 五个按钮的独立状态 @State private var topState: ButtonColorState = .blue @State private var leftState: ButtonColorState = .blue @State private var centerState: ButtonColorState = .blue @State private var rightState: ButtonColorState = .red @State private var bottomState: ButtonColorState = .blue var body: some View { ZStack { // 嵌套布局实现五按钮矩阵 VStack(spacing: 0) { // 顶部按钮 Button(action: { topState = topState.next() }) { Text("") .frame(width: 80, height: 40) .background(topState.color) .border(width: 2, edges: [.top, .leading, .trailing], color: .black) } HStack(spacing: 0) { // 左侧按钮 Button(action: { leftState = leftState.next() }) { Text("") .frame(width: 40, height: 40) .background(leftState.color) .border(width: 2, edges: [.leading, .bottom], color: .black) } // 中心按钮 Button(action: { centerState = centerState.next() }) { Text("") .frame(width: 40, height: 40) .background(centerState.color) .border(.black, width: 2) } // 右侧按钮 Button(action: { rightState = rightState.next() }) { Text("") .frame(width: 40, height: 40) .background(rightState.color) .border(width: 2, edges: [.trailing, .bottom], color: .black) } } // 底部按钮 Button(action: { bottomState = bottomState.next() }) { Text("") .frame(width: 80, height: 40) .background(bottomState.color) .border(width: 2, edges: [.leading, .trailing, .bottom], color: .black) } } // 十字分割线 VStack { Rectangle() .fill(.black) .frame(width: 2, height: 120) } HStack { Rectangle() .fill(.black) .frame(width: 120, height: 2) } } .frame(width: 120, height: 120) } }
2. 复用原有扩展与结构体
无需修改之前的View扩展和EdgeBorder结构体,直接复用即可。
修复说明
- 用
ButtonColorState枚举封装颜色切换逻辑,减少重复代码,提升可维护性 - 为五个按钮设置独立状态变量,点击时仅切换自身颜色,互不干扰
- 采用VStack嵌套HStack的布局,完美匹配预期的五按钮矩阵结构
- 替换旋转矩形为垂直+水平的十字分割线,还原预期视觉效果
- 调整每个按钮的边框设置,保证整体边框连贯统一
内容的提问来源于stack exchange,提问作者jdanvz
相关产品推荐
相关产品推荐

