轮播图片是APP中最常见的一种视图展现方式,我们可以通过内置的TabView轻松实现:
struct ContentView: View {
let width = UIScreen.main.bounds.width
let colors: [Color] = [.red, .blue, .green, .pink, .purple]
@State private var selection: Color = .red
var body: some View {
TabView(selection: $selection) {
ForEach(colors, id: .self) {
$0.tag($0)
.frame(width: width - 20, height: ($0 == selection) ? 200 : 150)
}
}
.frame(height: 200)
.tabViewStyle(PageTabViewStyle())
.animation(.spring(),value: selection)
}
}
虽然可以实现基本的效果,但想控制更多却不容易,所以我自己写了一下可以对其参数控制的轮播:
import SwiftUI
//轮播图Model
struct CarouselCard: Identifiable,Codable{
var id: Int
var name: String = ""
//安全初始化
fileprivate init(name: String, id: Int) {
self.name = name
self.id = id
}
}
//轮播图ViewModel
class CarouselViewModel: ObservableObject{
//轮播内容
var items = [CarouselCard]()
private func addItems(named name: String){
let item = CarouselCard(name: name, id: items.count)
items.insert(item, at: item.id)
}
init() {
if items.isEmpty {
for item in (0...5) {
addItems(named: "第(item)个")
}
}
}
}
struct Carousel: View {
@StateObject var carousels = CarouselViewModel()
@State var screenDrag:CGFloat = 1 //拖放时偏移
@State var activeCard = 0 //当前展示项
var numberOfItems:CGFloat{ CGFloat(carousels.items.count) } //轮播项总数
var cardWidth:CGFloat{ UIScreen.main.bounds.width - (CarouselConstants.widthOfHiddenCards * 2) - (CarouselConstants.spacing * 2 ) }
var body: some View {
let totalCanvasWidth: CGFloat = (cardWidth * numberOfItems) + (numberOfItems - 1) * CarouselConstants.spacing //容器总宽度=卡片*宽度 + 总间距
let xOffsetToShift = (totalCanvasWidth - UIScreen.main.bounds.width) / 2
let leftPadding = CarouselConstants.widthOfHiddenCards + CarouselConstants.spacing
let totalMovement = cardWidth + CarouselConstants.spacing
//当前正确位置
let activeOffset = xOffsetToShift + (leftPadding) - (totalMovement * CGFloat(activeCard))
//下一个位置
let nextOffset = xOffsetToShift + (leftPadding) - (totalMovement * CGFloat(activeCard) + 1)
//最终确定偏移位置
let calcOffset = (activeOffset != nextOffset) ? activeOffset + screenDrag : activeOffset
return HStack(spacing: CarouselConstants.spacing) {
ForEach(carousels.items) { item in
Text(item.name)
.frame(width: UIScreen.main.bounds.width - (CarouselConstants.widthOfHiddenCards * 2) - (CarouselConstants.spacing * 2),
height: (item.id == activeCard) ? CarouselConstants.cardHeight : CarouselConstants.cardHeight - 30)
.foregroundColor(.white)
.background(.black)
.cornerRadius(8)
.shadow(color: Color.gray, radius:4, x:0, y:4)
.onAppear{
activeCard = Int(numberOfItems / 2)//跳转到中间位置
}
}
}
.background(Color.white.edgesIgnoringSafeArea(.all))
.offset(x:calcOffset)
.gesture(panGesture())//拖动手势
.frame(maxWidth: .infinity)
.animation(.spring(),value:activeCard)//监听动画iOS 15
}
@GestureState private var gesturePanOffset:CGFloat = .zero //手势结束会恢复初值
//手势定义
private func panGesture() -> some Gesture{
DragGesture()
.updating($gesturePanOffset){ lastestGestureValue, _, _ in
screenDrag = lastestGestureValue.translation.width
}
.onEnded{ value in
screenDrag = 0
let moveOffset = UIScreen.main.bounds.width / 4 //超过屏幕宽度(计算后的值)才发生偏移
let lastIndex = carousels.items.endIndex - 1//数组最后一个索引值
//向右拖动
if value.translation.width > moveOffset{
activeCard = (activeCard <= 0) ? lastIndex : activeCard - 1
}
//向左拖动
if -value.translation.width > moveOffset{
activeCard = activeCard >= lastIndex ? 0 : activeCard + 1
}
let impactMed = UIImpactFeedbackGenerator(style: .medium)
impactMed.impactOccurred()
}
}
//参数控制器
private struct CarouselConstants{
static let spacing: CGFloat = 12 //与隐藏卡片的左右间距
static let widthOfHiddenCards: CGFloat = 20 //被隐藏卡片的宽度(左、右)
static let cardHeight: CGFloat = 160 //卡片高度
}
}
除非注明,网络人的文章均为原创,转载请以链接形式标明本文地址:https://www.55mx.com/post/166
《SwiftUI实现轮播图效果(旋转木马)》的网友评论(0)