You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SwiftUI中实现符合真实巴士布局的动态座位UI?

SwiftUI巴士座位UI优化实现

问题场景

需要实现模拟真实巴士的座位布局:左右两侧各设2列座位,中间保留过道,座位数据为API动态获取,但当前代码的布局无过道,座位排列不符合巴士的真实结构。

原代码与效果

原实现代码

import SwiftUI

struct BusSearchView: View {
    
    let seats: [Seat] = [Seat(id: 1, displayName: "1", isBooked: true),
                         Seat(id: 2, displayName: "2", isBooked: false),
                         Seat(id: 3, displayName: "3", isBooked: true),
                         Seat(id: 4, displayName: "4", isBooked: false),
                         Seat(id: 5, displayName: "5", isBooked: false),
                         Seat(id: 6, displayName: "6", isBooked: true),
                         Seat(id: 7, displayName: "7", isBooked: false),
                         Seat(id: 8, displayName: "8", isBooked: false),
                         Seat(id: 9, displayName: "9", isBooked: false),
    ]

        var body: some View {
            LazyVGrid(columns: [GridItem(), GridItem() , GridItem() , GridItem()]) {
                ForEach(seats) { seat in
                    Button(action: {
                        // Handle seat selection here
                        print(seat.displayName)
                    }) {
                        Text(seat.displayName)
                    }
                    .foregroundColor(seat.isBooked ? .gray : .black)
                }
            }
        }
}

struct Seat: Identifiable {
    let id: Int
    let displayName: String
    let isBooked: Bool
}

原效果截图

座位UI效果截图

优化方案

核心调整点

  1. 布局结构:采用HStack分隔左右座位区,中间间距作为过道,每个区域用双列LazyVGrid实现每排2个座位的排列
  2. 座位样式:给座位添加背景、圆角和禁用状态,更贴近真实座位视觉
  3. 动态数据适配:通过座位ID的模运算分组,适配API返回的连续ID座位数据(若ID不连续,可根据API返回的座位位置字段调整分组逻辑)

优化后代码

import SwiftUI

struct BusSearchView: View {
    // 模拟API动态获取的座位数据
    let seats: [Seat] = [Seat(id: 1, displayName: "1", isBooked: true),
                         Seat(id: 2, displayName: "2", isBooked: false),
                         Seat(id: 3, displayName: "3", isBooked: true),
                         Seat(id: 4, displayName: "4", isBooked: false),
                         Seat(id: 5, displayName: "5", isBooked: false),
                         Seat(id: 6, displayName: "6", isBooked: true),
                         Seat(id: 7, displayName: "7", isBooked: false),
                         Seat(id: 8, displayName: "8", isBooked: false),
                         Seat(id: 9, displayName: "9", isBooked: false),
                         Seat(id: 10, displayName: "10", isBooked: true)
    ]

    var body: some View {
        ScrollView {
            HStack(spacing: 40) {
                // 左侧座位区:每排2个座位
                LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 16) {
                    ForEach(seats.filter { seat in
                        seat.id % 4 == 1 || seat.id % 4 == 2
                    }) { seat in
                        SeatButton(seat: seat)
                    }
                    // 补全最后一排空位(可选,根据需求调整)
                    if seats.filter({ $0.id % 4 == 1 || $0.id % 4 == 2 }).count % 2 != 0 {
                        Color.clear.frame(height: 60)
                    }
                }

                // 右侧座位区:每排2个座位
                LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 16) {
                    ForEach(seats.filter { seat in
                        seat.id % 4 == 3 || seat.id % 4 == 0
                    }) { seat in
                        SeatButton(seat: seat)
                    }
                }
            }
            .padding()
        }
    }
}

// 抽离座位按钮子视图,复用样式逻辑
struct SeatButton: View {
    let seat: Seat
    
    var body: some View {
        Button(action: {
            print("选中座位:\(seat.displayName)")
        }) {
            Text(seat.displayName)
                .font(.headline)
                .frame(width: 60, height: 60)
                .background(seat.isBooked ? Color.gray.opacity(0.5) : Color.blue.opacity(0.3))
                .foregroundColor(seat.isBooked ? .white : .black)
                .cornerRadius(8)
                .overlay(
                    RoundedRectangle(cornerRadius: 8)
                        .stroke(seat.isBooked ? Color.gray : Color.blue, lineWidth: 1)
                )
        }
        .disabled(seat.isBooked)
    }
}

struct Seat: Identifiable {
    let id: Int
    let displayName: String
    let isBooked: Bool
}

优化后效果说明

  • 左右两侧各2列座位,中间保留明显过道,符合真实巴士布局
  • 座位有可视化样式:已预订座位为灰色半透明,可选座位为蓝色半透明,带边框区分
  • 支持动态数据:只要API返回的座位ID连续,即可自动分组到对应区域;若ID不连续,可修改filter逻辑(比如根据API返回的row和side字段分组)

内容的提问来源于stack exchange,提问作者Noob Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 12:44:50