You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS 11:如何在viewDidAppear中同步显示UIAlertController并等待点击

解决iOS 11中viewDidAppear同步显示UIAlertController的问题

兄弟,你遇到的这个问题我之前也踩过坑!核心原因其实是UIKit的运行机制viewDidAppear是在主线程的视图渲染周期里被调用的,如果你直接在这里同步阻塞主线程(比如等着用户点弹窗按钮),会彻底打断UIKit的渲染流程——弹窗根本来不及绘制出来,界面自然就僵住了。

直接说解决方案,分两种场景给你参考:

1. 最推荐:遵循UIKit回调驱动模式(异步逻辑拆分)

这是最符合iOS设计理念的写法,把用户点击后要执行的代码放到弹窗按钮的action回调里,完全不用纠结“同步”的问题:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    // 先创建弹窗
    let alert = UIAlertController(title: "提示", message: "请点击按钮继续操作", preferredStyle: .alert)
    // 添加按钮并绑定后续逻辑
    let confirmAction = UIAlertAction(title: "确定", style: .default) { [weak self] _ in
        // 用户点击后,执行你原本要“同步继续”的代码
        self?.executePostTapLogic()
    }
    alert.addAction(confirmAction)
    
    // 用DispatchQueue.main.async把present放到当前runloop的下一个周期
    // 避免和viewDidAppear的视图初始化流程冲突
    DispatchQueue.main.async {
        self.present(alert, animated: true, completion: nil)
    }
}

private func executePostTapLogic() {
    // 这里写用户点击后的所有逻辑
    print("用户已确认,开始执行后续操作")
    // 比如刷新数据、跳转页面等等
}

2. 模拟“同步等待”效果(不推荐,但满足需求)

如果你非要实现“等用户点击后再继续”的直观写法,可以用DispatchSemaphore,但绝对不能在主线程调用wait()(会卡死),必须放到子线程等待,主线程处理弹窗:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    let semaphore = DispatchSemaphore(value: 0)
    
    // 主线程present弹窗
    DispatchQueue.main.async {
        let alert = UIAlertController(title: "提示", message: "请点击按钮", preferredStyle: .alert)
        let confirmAction = UIAlertAction(title: "确定", style: .default) { _ in
            // 用户点击后发信号,唤醒等待的子线程
            semaphore.signal()
        }
        alert.addAction(confirmAction)
        self.present(alert, animated: true, completion: nil)
    }
    
    // 子线程等待信号,不阻塞主线程
    DispatchQueue.global().async {
        semaphore.wait()
        // 等待结束后,切回主线程执行UI相关的后续逻辑
        DispatchQueue.main.async {
            self.executePostTapLogic()
        }
    }
}

private func executePostTapLogic() {
    print("等待结束,继续执行")
}

关键提醒

  • 为什么直接present不行?因为viewDidAppear执行时,UIKit还在处理视图的布局和显示流程,直接调用present会打乱这个周期,弹窗无法被正常渲染。用DispatchQueue.main.asyncpresent推迟到当前runloop结束后执行,就能确保视图已经完全准备好,弹窗正常显示。
  • 第二种方法虽然模拟了同步等待,但本质还是异步逻辑,而且子线程等待的写法容易产生隐蔽的线程问题,除非特殊需求,优先用第一种回调驱动的写法。

内容的提问来源于stack exchange,提问作者M.Gonbe

火山引擎 最新活动