如何用SwiftUI复刻macOS DNS配置对话框?
如何实现与macOS DNS配置窗口一致的SwiftUI对话框?
需求回顾
- 支持通过+/-按钮添加、删除行
- 单击行即可选中该行
- 双击行直接进入编辑状态
之前尝试用带自定义单元格的Table()实现,但外观与行为和原生DNS对话框差异较大,希望找到更贴合的SwiftUI实现方案。
核心SwiftUI特性与实现技巧
1. 用List替代Table逼近原生风格
macOS原生DNS配置窗口的列表属于轻量单列样式,SwiftUI的List在macOS上的默认渲染更贴近系统原生NSTableView的视觉风格,相比Table更容易模拟DNS窗口的简洁感。
2. 行选中状态的原生还原
通过@State变量记录选中行的ID,给单元格添加background修饰器,使用系统原生选中背景色Color(NSColor.selectedContentBackgroundColor)实现高亮,完全匹配系统控件的选中视觉效果。
3. 双击触发编辑的交互逻辑
自定义单元格视图,通过@Binding绑定数据项,用onTapGesture(count:2)触发编辑状态切换;编辑时直接显示TextField并自动聚焦,按下Enter或失去焦点时自动退出编辑,无需额外的Done按钮,完全复刻原生交互。
4. +/-按钮的规范实现
在列表下方放置+/-按钮组,点击+按钮添加空数据项并自动进入编辑状态;点击-按钮删除当前选中行,且在无选中行时自动禁用删除按钮,符合系统控件的交互规范。
修改后的示例代码
import SwiftUI struct DNSItem: Identifiable { let id = UUID() var address: String } struct DNSConfigView: View { @State private var dnsItems: [DNSItem] = [ DNSItem(address: "8.8.8.8"), DNSItem(address: "8.8.4.4") ] @State private var selectedItemID: UUID? @State private var editingItemID: UUID? var body: some View { VStack(spacing: 12) { Text("DNS服务器") .font(.headline) List($dnsItems) { $item in DNSCell(item: $item, isSelected: selectedItemID == item.id, editingItemID: $editingItemID) .onTapGesture { selectedItemID = item.id } } .listStyle(.plain) .frame(height: 150) HStack(spacing: 8) { Button(action: addDNS) { Image(systemName: "plus") } Button(action: removeDNS) { Image(systemName: "minus") } .disabled(selectedItemID == nil) } .buttonStyle(.borderless) } .padding() .frame(width: 400) } private func addDNS() { let newItem = DNSItem(address: "") dnsItems.append(newItem) selectedItemID = newItem.id editingItemID = newItem.id } private func removeDNS() { guard let id = selectedItemID else { return } dnsItems.removeAll { $0.id == id } selectedItemID = dnsItems.last?.id } } struct DNSCell: View { @Binding var item: DNSItem let isSelected: Bool @Binding var editingItemID: UUID? @FocusState private var isTextFieldFocused: Bool var body: some View { HStack { if editingItemID == item.id { TextField("输入DNS地址", text: $item.address) .focused($isTextFieldFocused) .onAppear { isTextFieldFocused = true } .onSubmit { editingItemID = nil } .onChange(of: isTextFieldFocused) { newValue in if !newValue { editingItemID = nil } } } else { Text(item.address.isEmpty ? "未输入地址" : item.address) .foregroundColor(item.address.isEmpty ? .secondary : .primary) } } .padding(4) .background(isSelected ? Color(NSColor.selectedContentBackgroundColor) : Color.clear) .cornerRadius(4) .onTapGesture(count: 2) { editingItemID = item.id } } } // 预览 struct DNSConfigView_Previews: PreviewProvider { static var previews: some View { DNSConfigView() } }
关键细节说明
- 视觉统一:使用系统原生颜色常量保证选中背景、文字样式与macOS系统控件完全一致。
- 交互流畅:双击后自动聚焦输入框,编辑完成后自动退出,完全贴合原生DNS窗口的操作逻辑。
- 状态处理:空地址显示灰色提示文字,删除按钮随选中状态自动禁用,提升用户体验的同时符合系统规范。
内容的提问来源于stack exchange,提问作者Danny
相关产品推荐
相关产品推荐

