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

监听ViewWillAppear(从RxSwift迁移到Combine)

Combine实现UIViewController的viewWillAppear监听及过滤

以下是对应RxSwift实现的Combine版本,通过扩展UIViewController提供viewWillAppearPublisher,实现监听viewWillAppear事件并过滤图片查看器已展示的场景:

import Combine
import UIKit

private var viewWillAppearSubjectKey: UInt8 = 0

extension UIViewController {
    /// 监听viewWillAppear事件的Publisher,自动过滤图片查看器已展示的情况
    var viewWillAppearPublisher: AnyPublisher<Void, Never> {
        // 为每个ViewController实例关联独立的PassthroughSubject
        let subject = associatedObject(forKey: &viewWillAppearSubjectKey) {
            PassthroughSubject<Void, Never>()
        }
        
        // 仅执行一次方法交换,hook viewWillAppear生命周期方法
        static let swizzleViewWillAppear: Void = {
            let originalSelector = #selector(UIViewController.viewWillAppear(_:))
            let swizzledSelector = #selector(UIViewController.swizzled_viewWillAppear(_:))
            
            guard let originalMethod = class_getInstanceMethod(Self.self, originalSelector),
                  let swizzledMethod = class_getInstanceMethod(Self.self, swizzledSelector) else {
                return
            }
            
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }()
        
        _ = Self.swizzleViewWillAppear
        
        return subject
            .filter { [weak self] _ in
                // 过滤图片查看器已展示的场景
                !(self?.isImageViewerPresented ?? false)
            }
            .eraseToAnyPublisher()
    }
    
    // 关联对象辅助方法,存储每个实例的Subject
    private func associatedObject<T>(forKey key: UnsafeRawPointer, initialValue: () -> T) -> T {
        if let existing = objc_getAssociatedObject(self, key) as? T {
            return existing
        }
        let newValue = initialValue()
        objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        return newValue
    }
    
    // 交换后的viewWillAppear方法,执行原逻辑后发送事件
    @objc private func swizzled_viewWillAppear(_ animated: Bool) {
        // 调用原方法(因方法已交换,此处实际执行系统原生viewWillAppear逻辑)
        swizzled_viewWillAppear(animated)
        
        // 向Subject发送事件
        let subject = objc_getAssociatedObject(self, &viewWillAppearSubjectKey) as? PassthroughSubject<Void, Never>
        subject?.send(())
    }
}

// 需在ViewController中实现该属性,用于判断图片查看器是否展示
extension UIViewController {
    var isImageViewerPresented: Bool {
        // 替换为你的实际判断逻辑,例如检查presentedViewController是否为图片查看器类型
        return presentedViewController is ImageViewerViewController
    }
}

使用示例

在ViewController中订阅这个Publisher:

class DemoViewController: UIViewController {
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        viewWillAppearPublisher
            .sink { [weak self] in
                // 处理viewWillAppear事件,此时图片查看器未处于展示状态
                self?.refreshPageContent()
            }
            .store(in: &cancellables)
    }
    
    private func refreshPageContent() {
        // 执行你的业务逻辑
    }
}

实现说明

  • 方法交换(Method Swizzling):Hook系统viewWillAppear(_:)方法,在原生命周期逻辑执行后发送事件,不干扰原有流程
  • 关联对象:为每个ViewController实例独立存储PassthroughSubject,避免不同实例间的事件串扰
  • 过滤逻辑:通过filter操作符自动排除图片查看器已展示的场景,和RxSwift实现逻辑完全对齐
  • 类型擦除:返回AnyPublisher隐藏内部实现细节,符合Combine最佳实践

内容的提问来源于stack exchange,提问作者bumseok park

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 04:10:32