包装的SwiftUI视图的子类行为

时间:2020-01-11 16:29:30

标签: swift swiftui

我将尝试通过示例来说明我要寻找的内容。


比方说,我实现了一个加载图像的概念,看起来像这样:

struct AsyncImage: View {
    @ObservedObject private var imageDownloader: ImageDownloader

    init(url: URL) {
        imageDownloader = ImageDownloader(url: url)
    }

    var body: Image {
        Image(uiImage: imageDownloader.image)
    }
}

class ImageDownloader: ObservableObject {
    @Published var image: UIImage

    init(url: URL) { /* ... */ }
    // implementation details unimportant
}

以上实现允许通过应用轻松重用:

struct UserDetailsView: View {
    var body: some View {
        VStack {
            AsyncImage(url: userProfileUrl).resizable().aspectRatio(16.0 / 9.0, contentMode: .fit)
            Text(user.name)
        }
    }
}

但是上述代码无法编译,因为resizable()只是Image的一部分。如何以一种干净的方式使此功能可用?

在结构中添加isResizable标志可以解决该问题,但是该解决方案既不可扩展,也不向前兼容。

使用PublisherViewModifier可能有效,但是我无法将这些概念结合起来。


有任何解决此问题的建议吗?还是我试图以错误的方式解决这个问题?

2 个答案:

答案 0 :(得分:1)

方法可能如下-将所需的修饰符代理到内部包装的dat <- structure(list(Candidates = structure(c(6L, 5L, 7L, 4L, 3L, 2L, 1L), .Label = c("Andrew W.", "Anthony E.", "Hanna F.", "Ignacio R.", "John R.", "Liza S.", "Peter F."), class = "factor"), Municipality = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 2L), .Label = c("A", "B"), class = "factor"), Votes = c(100, 120, 140, 110, 335, 225, 400)), class = "data.frame", row.names = c(NA, -7L)) 中。不理想,但在某些情况下值得考虑

Image

因此struct AsyncImage: View { @ObservedObject private var imageDownloader: ImageDownloader private let resizing: Bool init(url: URL, resizalbe: Bool = false) { imageDownloader = ImageDownloader(url: url) resizing = resizalbe } var body: some View { var image = Image(uiImage: imageDownloader.image) if resizing { image = image.resizable() } return image } func resizable() -> Self { AsyncImage(url: imageDownloader.url, resizalbe: true) } } 可以编译。

答案 1 :(得分:1)

我最终得到的解决方案是删除整个AsyncImage概念。毕竟,ImageDownloader可能会使用某些共享的全局状态(例如某个地方的经理),因此将其隐藏在AsyncImage中并不是一个好选择。比隐式更好的显式:)

相反,我将下载程序上移了一个级别:

struct UserDetailsView: View {
    @ObservedObject var imageDownloader: ImageDownloader

    var body: some View {
        VStack {
            Image(uiImage: imageDownloader.image).resizable().aspectRatio(16.0 / 9.0, contentMode: .fit)
            Text(user.name)
        }
    }
}

此方法具有多个优点:

  • 直接访问所有Image修饰符
  • 还可以从上游注入
  • downloader
  • 可重用性不受影响-我重用AsyncImage而不是重用ImageDownloader

我的看法:尝试通过混合业务代码和表示代码来使可重用组件不是一个好方法。胜利的单一责任原则:)