繁体   English   中英

Swift 异步等待:如何与多个非异步委托一起使用?

[英]Swift async await: how to use with several non async delegates?

我创建了这个简单的示例,它是一个具有自动完成功能的UITextField ,显示了一个表格视图,其中显示了随着用户在文本字段中键入而演变的异步数据。

文本域

import UIKit

protocol TextFieldDelegate {
    func autocompletedComponents(
        _ textField: TextField,
        _ components: @escaping ([String]) -> Void
    )
}

class TextField: UITextField {
    var components: [String] = []
    var tableView = UITableView(frame: .zero)
    var autocompletionDelegate: TextFieldDelegate? { didSet { setupUI() } }
    
    // Actions
    @objc private func didUpdateText() {
        autocompletionDelegate?.autocompletedComponents(self) { [weak self] components in
            guard let weakSelf = self else {
                return
            }
            weakSelf.components = components
            weakSelf.tableView.reloadData()
            weakSelf.updateUI()
        }
    }
    
    // Event
    override func becomeFirstResponder() -> Bool {
        tableView.isHidden = false
        return super.becomeFirstResponder()
    }
    
    override func resignFirstResponder() -> Bool {
        tableView.isHidden = true
        return super.resignFirstResponder()
    }
    
    // Init
    override init(frame: CGRect) {
        super.init(frame: frame)
        internalInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        internalInit()
    }
    
    private func internalInit() {
        addTarget(
            self,
            action: #selector(didUpdateText),
            for: .editingChanged
        )
    }
    
    // UI
    private func setupUI() {
        tableView.dataSource = self
        tableView.delegate = self
        tableView.showsVerticalScrollIndicator = false
        tableView.removeFromSuperview()
        superview?.addSubview(tableView)
    }
    
    private func updateUI() {
        let tableViewHeight = Double(min(5, max(0, components.count))) * 44.0
        tableView.frame = CGRect(
            origin: CGPoint(
                x: frame.origin.x,
                y: frame.origin.y + frame.size.height
            ),
            size: CGSize(
                width: frame.size.width,
                height: tableViewHeight
            )
        )
    }
}

extension TextField: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = components[indexPath.row]
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        44.0
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        components.count
    }
}

视图控制器

import MapKit
import UIKit

class ViewController: UIViewController {
    private var completion: (([MKLocalSearchCompletion]) -> Void)?
    private var searchCompleter = MKLocalSearchCompleter()
    
    @IBOutlet weak var textField: TextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        searchCompleter.delegate = self
        textField.autocompletionDelegate = self
    }
}

extension ViewController: TextFieldDelegate {
    func autocompletedComponents(
        _ textField: TextField,
        _ components: @escaping ([String]) -> Void
    ) {
        if completion == nil {
            completion = { results in
                components(results.map { $0.title })
            }
        }
        searchCompleter.queryFragment = textField.text ?? ""
    }
}

extension ViewController: MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        completion?(completer.results)
    }
}

在此示例中,视图控制器使用来自MapKit的数据。 现在我想摆脱@escaping块并用新的 async/await 语法替换它们。

我已经开始重写TextField代码:

protocol TextFieldDelegate {
    func autocompletedComponents(
        _ textField: TextField
    ) async -> [String]
}

@objc private func didUpdateText() {
    Task {
        let autocompletedComponents = await autocompletionDelegate?.autocompletedComponents(self) ?? []
        components = autocompletedComponents
        tableView.reloadData()
        updateUI()
    }
}

但是我被困在 ViewController 中,因为我不知道如何处理我直到现在使用的完成块。

谢谢您的帮助

这是一个使用CombinePassThroughSubject将数组从MKLocalSearchCompleterDelegate发送到您的autocompletedComponents函数的实现,该函数然后返回该数组以在TextField中使用

ViewController中:

class ViewController: UIViewController {
    private var searchCompleter = MKLocalSearchCompleter()
    var cancellables = Set<AnyCancellable>()
    var publisher = PassthroughSubject<[String], Never>()
    @IBOutlet weak var textField: TextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        searchCompleter.delegate = self
        textField.autocompletionDelegate = self
    }
}

extension ViewController: TextFieldDelegate {
    func autocompletedComponents(
        _ textField: TextField
    ) async -> [String] {
        searchCompleter.queryFragment = textField.text ?? ""
        return await withCheckedContinuation { continuation in
            publisher
                .sink { array in
                    continuation.resume(returning: array)
                }.store(in: &cancellables)
        }
    }
}

extension ViewController: MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        publisher.send(completer.results.map({$0.title}))
    }
}

或者你可以使用你的completion闭包,它看起来像这样:

func autocompletedComponents(
    _ textField: TextField,
    _ components: @escaping ([String]) -> Void
) {
    return await withCheckedContinuation { continuation in
        if completion == nil {
            completion = { results in
                continuation.resume(returning: results.map { $0.title })
            }
        }
        searchCompleter.queryFragment = textField.text ?? ""
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM