使用 SwiftUI 使自定义 UIView 方面比例适合/填充的麻烦
Posted
技术标签:
【中文标题】使用 SwiftUI 使自定义 UIView 方面比例适合/填充的麻烦【英文标题】:Trouble to make a custom UIView aspect scale fit/fill with SwiftUI 【发布时间】:2019-09-17 07:37:17 【问题描述】:SwiftUI 中没有公共 API 来响应 View 协议的 resizable 修饰符。只有 SwiftUI 中的 Image 可以使用 .resizable()。像 UIView for GIF 这样的自定义 UIView 现在无法调整大小。
我使用 SDWebImageSwiftUI AnimatedImage,它支持 UIKit View SDAnimatedImageView。 AnimatedImage 不响应 .resizable()、.scaleToFit、.aspectRatio(contentMode: .fit) 等。WebImage 支持 SwiftUI Image,所以它工作正常。
import SwiftUI
import SDWebImageSwiftUI
struct ContentView: View
let url = URL(string: "https://media.giphy.com/media/H62DGtBRwgbrxWXh6t/giphy.gif")!
var body: some View
VStack
AnimatedImage(url: url)
.scaledToFit()
.frame(width: 100, height: 100)
WebImage(url: url)
.scaledToFit()
.frame(width: 100, height: 100)
不确定这是否是 Apple 错误。期望像 SDWebImageSwiftUI AnimatedImage 这样的自定义视图能够响应 SwiftUI 大小相关的修饰符,如 .scaledToFit()。
相关问题:https://github.com/SDWebImage/SDWebImageSwiftUI/issues/3
【问题讨论】:
您可以在View
上使用.scaledToFit
,因为Image
可能嵌入在孩子的某个地方。但是ViewRepresentable
没有这个机会,因此您可能必须自己实现一个方法来设置适当的纵横比(从中返回修改后的视图)。我还没有看到任何从 EnvrionmentValues 检索纵横比的方法。
问题是 AnimatedImage(url: url) 没有 .resisable() 修饰符。
我尝试在 Rectangle 和 VStack 上使用 scaledToFit,但也没有效果。
【参考方案1】:
SwiftUI 使用抗压缩优先级和内容拥抱优先级来决定可以调整的大小。
如果您想将视图的大小调整到低于其固有内容大小,则需要降低抗压缩优先级。
例子:
func makeUIView(context: Context) -> UIView
let imageView = UIImageView(image: UIImage(named: "yourImage")!)
imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
imageView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
return imageView
这将允许您将.frame(width:height:)
设置为您想要的任何大小。
【讨论】:
这和覆盖 layoutSubviews 都有效。我更喜欢这个,因为我不需要覆盖任何东西。【参考方案2】:终于找到了解决办法。
在 SDAnimationImageView 或 UIImageView 之外创建一个 UIView 包装器,然后覆盖 layoutSubviews() 设置子视图的框架。
这是我的完整代码。
而SDWebImageSwiftUI 也发布了一个新版本,它使用 wrapper 来解决这个问题。
class ImageModel: ObservableObject
@Published var url: URL?
@Published var contentMode: UIView.ContentMode = .scaleAspectFill
struct WebImage: UIViewRepresentable
@ObservedObject var imageModel = ImageModel()
func makeUIView(context: UIViewRepresentableContext<WebImage>) -> ImageView
let uiView = ImageView(imageModel: imageModel)
return uiView
func updateUIView(_ uiView: ImageView, context: UIViewRepresentableContext<WebImage>)
uiView.imageView.sd_setImage(with: imageModel.url)
uiView.imageView.contentMode = imageModel.contentMode
func url(_ url: URL?) -> Self
imageModel.url = url
return self
func scaledToFit() -> Self
imageModel.contentMode = .scaleAspectFit
return self
func scaledToFill() -> Self
imageModel.contentMode = .scaleAspectFill
return self
class ImageView: UIView
let imageView = UIImageView()
init(imageModel: ImageModel)
super.init(frame: .zero)
addSubview(imageView)
override func layoutSubviews()
super.layoutSubviews()
imageView.frame = bounds
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
【讨论】:
谢谢,应用了类似的解决方案并按预期工作:)以上是关于使用 SwiftUI 使自定义 UIView 方面比例适合/填充的麻烦的主要内容,如果未能解决你的问题,请参考以下文章
如何使自定义 UIView 与 UINavigationBar 结合使其看起来像一个
SwiftUI - 使用自定义绘图包装 UIView 时出现 UIViewRepresentable 故障