我使用
ScrollView
和 LazyHStack
创建了一个轮播视图,如下所示:
struct CarouselView<Content: View>: View {
let content: Content
@State private var currentIndex = 0
@State private var contentSize: CGSize = .zero
private let showsIndicators: Bool
private let spacing: CGFloat
private let shouldSnap: Bool
init(showsIndicators: Bool = true,
spacing: CGFloat = .zero,
shouldSnap: Bool = false,
@ViewBuilder content: @escaping () -> Content) {
self.content = content()
self.showsIndicators = showsIndicators
self.spacing = spacing
self.shouldSnap = shouldSnap
}
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: showsIndicators) {
LazyHStack(spacing: spacing) {
content
}.apply {
if #available(iOS 17.0, *), shouldSnap {
$0.scrollTargetLayout()
} else {
$0
}
}
}
.clipped() // tried to add this to avoid clipping
.apply {
if #available(iOS 17.0, *), shouldSnap {
$0.scrollTargetBehavior(.viewAligned)
} else {
$0
}
}
}
}
}
extension View {
func apply<V: View>(@ViewBuilder _ block: (Self) -> V) -> V { block(self) }
}
然后我按如下方式使用它:
struct ContentView: View {
let imagesNames = ["img-1", "img-2", "img-3", "img-4"]
let numberOfLines = [2, 1, 3, 2]
var body: some View {
VStack(spacing: 40) {
continuousCarousel
}
}
private var continuousCarousel: some View {
CarouselView(showsIndicators: true,
spacing: 20) {
ForEach(0 ..< imagesNames.count, id: \.self) { index in
createImageTile(with: imagesNames[index],
height: 70,
numberOfLines: numberOfLines[index])
}
}
}
private func createImageTile(with image: String,
height: CGFloat,
numberOfLines: Int) -> some View {
VStack(spacing: .zero) {
Image(image)
.resizable()
.cornerRadius(30)
.scaledToFit()
.aspectRatio(contentMode: .fill)
.frame(width: 200, height: 100)
.padding(.bottom, 30)
Text("Headline")
.bold()
.padding(.bottom, 10)
ForEach(0 ..< numberOfLines, id: \.self) { _ in
Text("Some description here")
.padding(.bottom, 5)
}
Spacer() // added to top align content
}
}
}
这给了我大部分想要的结果,但是,正如你所看到的,图像的顶部被安全区域剪掉了:
我尝试按照此
answer的建议将
clipped
修饰符添加到滚动视图,但无济于事。
我该如何解决这个问题?
我认为你只需要更改函数中应用于
Image
的修饰符 createImageTile
:
.aspectRatio(contentMode: .fill)
。这会覆盖之前的修饰符 .scaledToFit()
,因此是多余的。.cornerRadius
已弃用,建议使用
.clipShape
代替。
height
参数并未被使用。这是使用缩放填充的更新版本。然后使用圆角矩形将图像剪切到包含帧 (200x100)。
Image(image)
.resizable()
// .cornerRadius(30)
// .scaledToFit()
// .aspectRatio(contentMode: .fill)
.scaledToFill()
.frame(width: 200, height: 100)
.clipShape(RoundedRectangle(cornerRadius: 30))
.padding(.bottom, 30)