SwiftUI + Combine:如何将数据分配给带有动画的模型

Posted

技术标签:

【中文标题】SwiftUI + Combine:如何将数据分配给带有动画的模型【英文标题】:SwiftUI + Combine: how to assign data to a model with animation 【发布时间】:2021-07-28 12:24:25 【问题描述】:

我正在使用Combine 来获取用户在搜索字段中键入的匹配项。它在去抖动、去重和获取数据方面效果很好。但我无法为项目的条目设置动画。当匹配到达时,ObservedObject 会立即更新,并且 UI 已经显示匹配。我更愿意为匹配项设置动画。

我认为问题在于匹配是在assign 方法中设置的,缺少withAnimation。是否有可能以某种方式在Combine 管道中添加动画?我应该为此使用副作用吗?

代码如下:

class DictionarySearchViewModel: ObservableObject         
        @Published var translationMatches = [TranslationMatch]()
        @Published var text: String = ""
        @Published var isLoading = true
        @Published var connectionError = false
        
        private var cancellable: AnyCancellable? = nil
        
        init() 
            cancellable = $text
                .debounce(for: .seconds(0.1), scheduler: DispatchQueue.main)
                .removeDuplicates()
                .map  [self] queryText -> AnyPublisher<[TranslationMatch], Never> in
                    
                    if queryText.count < 2 
                        return Future<[TranslationMatch], Never>  promise in
                            promise(.success([TranslationMatch]()))
                        
                        .eraseToAnyPublisher()
                     else 
                            
                            return DictionaryService.sharedInstance()
                                .searchMatchesPublisher(queryText)
                                .retry(3)
                                .replaceError(with: [TranslationMatch]())
                                .eraseToAnyPublisher()
                         else 
                            return Future<[TranslationMatch], Never>  promise in
                                promise(.success([TranslationMatch]()))
                            
                            .eraseToAnyPublisher()
                        
                    
                
                .switchToLatest()
                .eraseToAnyPublisher()
                .receive(on: DispatchQueue.main)
                .assign(to: \.translationMatches, on: self)
        



struct DictionarySearchView: View 
    @ObservedObject var viewModel: DictionarySearchViewModel

    var body: some View 
        ScrollView(.vertical, showsIndicators: false) 
            VStack(alignment: .leading, spacing: 15) 
                ForEach(viewModel.translationMatches, id: \.id)  translationMatch in
                     Text(translationMatch.label)
                
            
        
    

【问题讨论】:

不要使用.assign(to: \.translationMatches, on: self) - 它会创建对self 的强引用 - 改为使用assign(to:)。但是如果仍然不能使用动画,那么你可以使用sink 来代替 【参考方案1】:

ForEach 中的Text 上使用.animation.transition

 Text(translationMatch.label)
   .animation(.default)
   .transition(AnyTransition.move(edge: .top))
           

https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions

【讨论】:

你基本上是对的,但是动画修饰符应该在你指定一个过渡之后指定。在您上面编写的代码中,它将执行一个没有过渡的默认动画。

以上是关于SwiftUI + Combine:如何将数据分配给带有动画的模型的主要内容,如果未能解决你的问题,请参考以下文章

使用 Combine 和 SwiftUI 在 Realm 中观察收集结果

如何使用 Combine 框架和 SwiftUI 发布网络请求的数据

如何使用 SwiftUI 和 Combine 检测 Datepicker 的值变化?

使用 SwiftUI、Combine 和 Firebase,我如何在将用户的帐户链接到电子邮件/密码之前验证用户是不是已匿名登录?

使用 SwiftUI/Combine,如何避免在 ViewModel 中放置可取消项

Combine + SwiftUI 中的最佳数据绑定实践?