我已经创建了一个 UIViewRepresentable
对于 UIVisualEffectView
以使某些组件充满活力。这很有效,但它似乎有时会垂直收缩控件,或者在运行时随机改变它们的边界。我似乎无法让它可靠地工作。我需要这个功能能在任何SwiftUI内容甚至是其他内容上工作。UIViewRepresentable
用来代替内容。包裹 UIVisualEffectView
内 UIView
并使用自动布局似乎有帮助,但其他控件(如自定义的 UILabel
裹挟 UIViewRepresnetable
被垂直剪切)。)
public struct VibrantView<Content: View>: UIViewRepresentable {
private let content: UIView!
private let vibrancyBlurEffectStyle: UIBlurEffect.Style
init(vibrancyBlurEffectStyle: UIBlurEffect.Style, @ViewBuilder content: () -> Content) {
self.content = UIHostingController(rootView: content()).view
self.vibrancyBlurEffectStyle = vibrancyBlurEffectStyle
}
public func makeUIView(context: Context) -> UIView {
let containerView = UIView()
let blurEffect = UIBlurEffect(style: vibrancyBlurEffectStyle)
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect)
let blurView = UIVisualEffectView(effect: vibrancyEffect)
blurView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(blurView)
content.backgroundColor = .clear
content.translatesAutoresizingMaskIntoConstraints = false
blurView.contentView.addSubview(content)
NSLayoutConstraint.activate([
blurView.widthAnchor.constraint(equalTo: containerView.widthAnchor),
blurView.heightAnchor.constraint(equalTo: containerView.heightAnchor),
content.widthAnchor.constraint(equalTo: blurView.widthAnchor),
content.heightAnchor.constraint(equalTo: blurView.heightAnchor),
])
content.setContentHuggingPriority(.defaultLow, for: .vertical)
content.setContentHuggingPriority(.defaultLow, for: .horizontal)
content.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
content.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
blurView.setContentHuggingPriority(.defaultLow, for: .vertical)
blurView.setContentHuggingPriority(.defaultLow, for: .horizontal)
blurView.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
blurView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
return containerView
}
public func updateUIView(_ uiView: UIView, context: Context) {
}
}
用作。
...
VibrantView(vibrancyBlurEffectStyle: .dark) {
Text("Hello")
.foregroundColor(Color.gray)
}
当在设备上运行时,与其他视图一起运行在一个... VStack
你会看到 "Hello "从底部被剪掉一部分。在预览中,你会看到 "Hello "周围有一个大得多的蓝色矩形(bounds),而我希望这个矩形能紧贴内容。该 VStack
不假定整体视图的全部自然高度。
使用 fixedSize()
不起作用,而且与其他控件一起使用时,会产生更奇怪的结果。
在尝试了各种技术和黑客之后--我根本无法让UIKit容器(即. VibrantView
)可靠地拥抱它的SwiftUI内容,而不添加一个固定大小的控件。.frame(...)
修饰符在上面--这使得它很难与动态大小的 Text
.
对我来说,我的工作是一个有点黑客的工作,可能不会对每一个通用的视图工作(可能不会对几十个视图进行很好的扩展),但对简单的用例很好,特别是如果你在一个动态大小的 UITableViewCell
.
我们的想法是使用相同视图的虚拟版本,并设置了 VibrantView
在...中 .overlay( ... )
. 这将迫使覆盖层与父SwitfUI的整体大小相同。View
. 由于所应用的视图是同一视图的副本,而该视图的 VibrantView
包裹,你最终在运行时和Xcode预览中得到正确的动态大小。
所以像这样。
SomeView()
.frame(minWidth: 0, maxWidth: .infinity)
.overlay(
VibrantView(vibrancyBlurEffectStyle: .dark) {
SomeView()
.frame(minWidth: 0, maxWidth: .infinity)
}
)
我可以想象把它变成一个修改器,这样它就能在一次调用中封装上面的内容,但在我的例子中,为了确保它对Images的性能,我正在做这样的事情。
Circle()
.foregroundColor(.clear)
.frame(width: 33, height: 33)
.overlay(
VibrantView(vibrancyBlurEffectStyle: .systemMaterialDark) {
Image("some image")
.resizable()
}
)
创建一个 Circle
与实际图像相比,可以说是轻车熟路。我创建了一个透明的圆圈,在那里设置图像的实际大小,然后再把 VibrantView
容器中的 overlay
.
为什么这么复杂?
试试这个。
struct Blur: UIViewRepresentable {
#if os(iOS)
var style: UIBlurEffect.Style = .systemMaterial
#else
var style: UIBlurEffect.Style = .light
#endif
init(_ style: UIBlurEffect.Style = .dark) {
self.style = style
}
func makeUIView(context: Context) -> UIVisualEffectView {
return UIVisualEffectView(effect: UIBlurEffect(style: style))
}
func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
uiView.effect = UIBlurEffect(style: style)
}
}
并使用:
Text("Great text with prominent")
.font(.largeTitle)
.padding()
.background(Blur(.prominent))
一般来说,你不应该在UIViewRepresentable ->中使用约束条件;大小将由父视图定义,比如本例中的Text或VStack,他们会给模糊提供合适的大小。通常模糊不是独立存在的,但典型的是模糊的背景,因为你想把文字放在上面,这样你就可以更好的阅读文字。