使用视图模型创建SwiftUI预览

8cdiaqws  于 2023-03-17  发布在  Swift
关注(0)|答案(2)|浏览(143)

我有一个SwiftUI视图,它有一个关联的视图模型。

struct BookmarksView: View {
    @StateObject private var viewModel = BookmarksViewModel()
    
    var body: some View {
        switch viewModel.viewState {
        case .empty:
            BookmarksEmptyView()
        case .content(let newsLetters):
            ListView(newsLetters: newsLetters)
        }
    }
}

使用下面的PreviewProvider,我只能测试empty的情况,而不能测试content的情况。

struct BookmarksView_Previews: PreviewProvider {
    static var previews: some View {
        BookmarksView()
    }
}

您能否建议一种方法来测试这两种情况下的BookmarksView(即emptycontent)?
谢谢洛伦佐

avkwfej4

avkwfej41#

您可以创建一个init,这样您就可以注入视图模型,但是使用默认值,这样您就不需要正常使用它。

init(viewModel: BookmarksViewModel = BookmarksViewModel() {
   _viewModel = StateObject(wrappedValue: viewModel)
}

现在,您可以在预览代码中创建和注入示例
对于预览,我会添加两个静态属性来创建不同的版本,使用预览中使用的视图模型的不同配置。我在#if DEBUG/#endif中这样做,这样它们就不会在发布版本中被错误地使用。

#if DEBUG
extension BookmarksViewModel {
    static let emptyState: BookmarksViewModel = {
        BookmarksViewModel(viewState: .empty)
    }()

    static let contentState: BookmarksViewModel = {
        let newsLetters = Newsletter.previews
        return BookmarksViewModel(viewState: .content(newsLetters))
    }()
}
#endif
  • 请注意,由于我不知道视图模型是如何声明的,所以我创建了自己的版本 *

然后可直接在预览中使用

struct BookmarksViewEmpty_Previews: PreviewProvider {
    static var previews: some View {
        BookmarksView(viewModel: .emptyState)
    }
}

struct BookmarksViewContent_Previews: PreviewProvider {
    static var previews: some View {
        BookmarksView(viewModel: .contentState)
    }
}

OP发布的解决方案是解决这个问题的另一个好方法,因为子视图现在与视图模型解耦,这使得创建预览变得更加容易,但是由于属性不会改变,因此不需要在预览中使用@State属性,相反,我们可以使用constant()创建绑定

struct BookmarksViewEmpty_Previews: PreviewProvider {
    static var previews: some View {
        BookmarksView(viewState: .constant(.empty))
    }
}

struct BookmarksViewContent_Previews: PreviewProvider {
    static var previews: some View {
        BookmarksView(viewState: .constant(.content(Newsletter.previews)))
    }
}
zd287kbt

zd287kbt2#

最好的方法是将视图模型的一个示例传递到视图中。您将能够根据需要配置视图模型。为此,您需要添加一个自定义init。但是要小心@StateObject(查看我关于@StateObject here的回答以了解更多细节)。

struct BookmarksView: View {
    @StateObject private var viewModel: BookmarksViewModel
    
    init(viewModel: @autoclosure @escaping () -> BookmarksViewModel) {
        _viewModel = StateObject(wrappedValue: viewModel())
    }

    var body: some View {
        switch viewModel.viewState {
        case .empty:
            BookmarksEmptyView()
        case .content(let newsLetters):
            ListView(newsLetters: newsLetters)
        }
    }
}

然后:

struct BookmarksView_Previews: PreviewProvider {
    static var previews: some View {
        BookmarksView(
            viewModel: BookmarksViewModel(initialState: . content([...]))
        )
    }
}

相关问题