多UIViewController数据依赖对象构建的设计模式方案
咱们来聊聊在多个嵌入UINavigationController的UIViewController场景里,怎么构建一个依赖这些控制器数据的对象——拿一个简化的机票购买Demo来举例,一看就懂。
首先先定义核心的Ticket结构体,它的字段分别由不同的VC提供数据:
struct Ticket { let originAirport: Airport // 由UIViewController 1提供数据 let destinationAirport: Airport // 由UIViewController 1提供数据 let departureDate: Date // 由UIViewController 2提供数据 let returnDate: Date? // 由UIViewController 2提供数据(可选,对应单程/往返) let passengerCount: Int // 由UIViewController 3提供数据 let cabinClass: CabinClass // 由UIViewController 3提供数据 } // 补充依赖的枚举定义 enum Airport: String { case PEK, SHA, CAN, SZX // 简化示例,代表国内主要机场 } enum CabinClass: String { case economy, business, first }
核心思路:用共享数据容器逐步填充
直接让每个VC各自持有部分数据再最后拼接太零散,咱们可以搞一个共享的临时数据容器,让所有参与流程的VC都能访问并更新这个容器,最后在流程收尾时组装成完整的Ticket对象。
1. 创建可变的临时数据草稿类
先做一个可以逐步填充的草稿类,还得加验证和组装逻辑:
class TicketDraft { var originAirport: Airport? var destinationAirport: Airport? var departureDate: Date? var returnDate: Date? var passengerCount: Int? var cabinClass: CabinClass? // 检查是否具备生成完整Ticket的条件 func canBuildTicket() -> Bool { return originAirport != nil && destinationAirport != nil && departureDate != nil && passengerCount != nil && cabinClass != nil } // 组装成最终Ticket,失败则抛出错误 func buildTicket() throws -> Ticket { guard let origin = originAirport, let dest = destinationAirport, let depDate = departureDate, let paxCount = passengerCount, let cabin = cabinClass else { throw TicketError.incompleteData } return Ticket(originAirport: origin, destinationAirport: dest, departureDate: depDate, returnDate: returnDate, passengerCount: paxCount, cabinClass: cabin) } enum TicketError: Error { case incompleteData } }
2. 在导航流程中传递草稿容器
启动购票流程时,先初始化草稿实例,然后把它传递给第一个VC:
// 假设从首页跳转进入购票流程 let ticketDraft = TicketDraft() let firstVC = AirportSelectionViewController(draft: ticketDraft) let navController = UINavigationController(rootViewController: firstVC) present(navController, animated: true)
每个VC持有草稿的引用,用户完成输入后直接更新对应字段,再跳转到下一个VC时继续传递草稿:
class AirportSelectionViewController: UIViewController { private let ticketDraft: TicketDraft init(draft: TicketDraft) { self.ticketDraft = draft super.init(nibName: nil, bundle: nil) } // 用户选好出发/到达机场后的回调 func onAirportsSelected(origin: Airport, dest: Airport) { ticketDraft.originAirport = origin ticketDraft.destinationAirport = dest // 跳转到日期选择VC,继续传递草稿 let dateVC = DateSelectionViewController(draft: ticketDraft) navigationController?.pushViewController(dateVC, animated: true) } }
3. 最终组装完整Ticket
当最后一个VC完成用户输入后,调用草稿的buildTicket()方法生成完整对象:
class PassengerSelectionViewController: UIViewController { private let ticketDraft: TicketDraft init(draft: TicketDraft) { self.ticketDraft = draft super.init(nibName: nil, bundle: nil) } // 用户点击确认按钮的逻辑 func onConfirmButtonTapped() { do { let finalTicket = try ticketDraft.buildTicket() // 这里可以发起购票请求、跳转到订单确认页等 print("成功生成机票订单:\(finalTicket)") } catch { // 处理数据不完整的情况,比如弹出提示 let alert = UIAlertController(title: "提示", message: "请完善所有必填信息", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) } } }
额外优化小建议
- 如果不想让VC直接持有草稿,可以用闭包回调的方式,让每个VC只负责收集自己的输入,通过回调把数据传递给上层,最后在导航根VC或者专门的协调器里组装。
- 对于更复杂的流程,推荐用Coordinator模式,把导航逻辑和数据组装逻辑从VC里剥离出来,让VC只专注于UI展示和用户交互,代码会更清晰易维护。
内容的提问来源于stack exchange,提问作者Hapeki
相关产品推荐
相关产品推荐

