SwiftUI极简教程26:构建一个Banner图片轮播(下)

简介: SwiftUI极简教程26:构建一个Banner图片轮播(下)

承接上一章的内容,我们继续实现下如何使用SwiftUI构建一个Banner轮播图。

上一章,我们使用HStack横向视图和Gestures手势做一个Banner轮播图,完成了基础的交互,但还不算全部完成。

这一章,我们将学习Banner轮播图的交互,包含移动Banner轮播图的动画,以及点击Banner轮播图进入详情页。


image.png

那么,我们开始吧。

Animation动画效果


我们通过GeometryReader几何视图的outerView设置了CardView卡片的大小,但它是固定的。


.frame(width: outerView.size.width, height: outerView.size.height)


我们了解下Banner轮播图的展示逻辑,它是当前显示的CardView卡片会大一些,切换的时候,另外的会小一些,当我们将卡片滑动到中间展示时,它又会放大。

我们要做的就是这个效果。


.frame(width: outerView.size.width, height: self.currentIndex == index ? 250 : 200)


我们可以尝试根据currentIndex当前索引位置来控制CardView卡片的高度,如果它在当前,那么height高度为250,如果不是,height高度为200

image.png

为了效果好看,我们还可以调整CardView卡片的透明度,不在当前展示的卡片,我们让它“模糊”一点,突出中间的卡片。


.opacity(self.currentIndex == index ? 1.0 : 0.7)
复制代码


最后,我们把动画效果加到整个GeometryReader几何视图中。


.animation(.interpolatingSpring(mass: 0.6, stiffness: 100, damping: 10, initialVelocity: 0.3),value: offset)
复制代码


我们开启了动画,动画呈现的方式为interpolatingSpring弹性旋转动画。

我们运行下模拟器预览下效果。

image.png

恭喜你,完成了Banner轮播图的动画效果!

DatailView详情页


下面,我们来完成下点击Banner轮播图进入DetailView详情页的交互。

首先创建一个新的页面,我们命名为DetailView.swift

image.png

下面,我们完成下DetailView页面的设计,它由一个标题、内容和按钮组成。


struct DetailView: View {
    let imageName: String
    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                VStack(alignment: .leading, spacing: 5) {
                    // 图片名称
                    Text(self.imageName)
                        .font(.system(.title, design: .rounded))
                        .fontWeight(.heavy)
                        .padding(.bottom, 30)
                    // 描述文字
                    Text("要想在一个生活圈中生活下去,或者融入职场的氛围,首先你要学习这个圈子的文化和发展史,并尝试用这个圈子里面的“话术”和他们交流,这样才能顺利地融入这个圈子。")
                        .padding(.bottom, 40)
                    // 按钮
                    Button(action: {
                    }) {
                        Text("知道了")
                            .font(.system(.headline, design: .rounded))
                            .fontWeight(.heavy)
                            .foregroundColor(.white)
                            .padding()
                            .frame(minWidth: 0, maxWidth: .infinity)
                            .background(Color.blue)
                            .cornerRadius(8)
                    }
                }
                .padding()
                .frame(width: geometry.size.width, height: geometry.size.height, alignment: .topLeading)
                .background(Color.white)
                .cornerRadius(15)
            }
        }
    }
}


image.png

交互逻辑


首先,我们先实现点击CardView打开DetailView详情页。

我们使用GeometryReader几何视图和ScrollView滚动视图搭建了一个DetailView详情页。

然后我们回到ContentView首页,创建一个点击状态。


@State var isShowDetailView = false


当我们点击CardView卡片时,进入到对应的详情页。和之前的章节一样我们在CardView卡片视图上添加点击事件,然后用ZStack层叠视图将DetailView详情页和ContentView首页叠加在一起。


//详情页
if self.isShowDetailView {
    DetailView(imageName: imageModels[currentIndex].imageName)
        .offset(y: 200)
        .transition(.move(edge: .bottom))
        .animation(.interpolatingSpring(mass: 0.5, stiffness: 100, damping: 10, initialVelocity: 0.3),value: offset)
}


image.png

当我们点击CardView卡片视图的时候,展示DetailView详情页。

image.png

当然,还远远不够,我们希望展示的效果是,Banner图片轮播在展示详情的时候,背景部分可以看到原先Banner轮播的图片,我们可以根据isShowDetailView的状态再调整下样式。

//如果点击就图片就移上去
.offset(y: self.isShowDetailView ? -innerView.size.height * 0.3 : 0)
//如果点击图片两边就不留边距
.padding(.horizontal, self.isShowDetailView ? 0 : 20)
//如果点击就图片调整大小
.frame(width: outerView.size.width, height: self.currentIndex == index ? (self .isShowDetailView ? outerView.size.height : 250) : 200)


image.png

我们发现一个交互问题,现在我们尝试拖动Banner图片轮播,它也是可以拖动的,这不是我们想要的效果。

我们可以按照上面的逻辑,再用isShowDetailView判断一下。


//如果没有被点击
!self.isShowDetailView ? 
    //代码块
:nil


image.png

好了,这样,我们在展示DetailView详情页时,就不用担心Banner轮播图被拖动了。

我们最后再加上一个关闭按钮,用于关闭DetailView详情页。


//关闭按钮
Button(action: {
    self.isShowDetailView = false
}) {
    Image(systemName: "xmark.circle.fill")
        .font(.system(size: 30))
        .foregroundColor(.black)
        .opacity(0.7)
        .contentShape(Rectangle())
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topTrailing)
.padding(.trailing)

image.png

恭喜你,完成了所有的编程!

我们回顾一下,上篇我们完成了ScrollView滚动视图创建Banner轮播图,后来我们发现不太可行。然后,中篇我们尝试使用HStack横向视图和Gestures手势做一个Banner轮播图,并完成了基本的交互。下篇我们继续完成了整个Banner轮播图的交互逻辑。

真心不容易啊。

ContentView完整代码


struct ContentView: View {
    @State var currentIndex = 0
    @GestureState var dragOffset: CGFloat = 0
    @State private var offset: CGFloat = .zero
    @State var isShowDetailView = false
    var body: some View {
        ZStack {
            //首页轮播图
            GeometryReader { outerView in
                HStack(spacing: 0) {
                    ForEach(imageModels.indices, id: \.self) { index in
                        GeometryReader { innerView in
                            CardView(image: imageModels[index].image, imageName: imageModels[index].imageName)
                                //如果点击就图片就移上去
                                .offset(y: self.isShowDetailView ? -innerView.size.height * 0.3 : 0)
                        }
                        //如果点击图片两边就不留边距
                        .padding(.horizontal, self.isShowDetailView ? 0 : 20)
                        .opacity(self.currentIndex == index ? 1.0 : 0.7)
                        //如果点击就图片调整大小
                        .frame(width: outerView.size.width, height: self.currentIndex == index ? (self .isShowDetailView ? outerView.size.height : 250) : 200)
                        //点击进入详情页
                        .onTapGesture {
                            self.isShowDetailView = true
                        }
                    }
                }
                .frame(width: outerView.size.width, height: outerView.size.height, alignment: .leading)
                .offset(x: -CGFloat(self.currentIndex) * outerView.size.width)
                .offset(x: self.dragOffset)
                // 拖动事件
                .gesture(
                    //如果没有被点击
                    !self.isShowDetailView ?
                    DragGesture()
                        .updating(self.$dragOffset, body: { value, state, transaction in
                            state = value.translation.width
                        })
                        .onEnded({ value in
                            let threshold = outerView.size.width * 0.65
                            var newIndex = Int(-value.translation.width / threshold) + self.currentIndex
                            newIndex = min(max(newIndex, 0), imageModels.count - 1)
                            self.currentIndex = newIndex
                        })
                    : nil
                )
            }
            .animation(.interpolatingSpring(mass: 0.6, stiffness: 100, damping: 10, initialVelocity: 0.3),value: offset)
            //详情页
            if self.isShowDetailView {
                DetailView(imageName: imageModels[currentIndex].imageName)
                    .offset(y: 200)
                    .transition(.move(edge: .bottom))
                    .animation(.interpolatingSpring(mass: 0.5, stiffness: 100, damping: 10, initialVelocity: 0.3),value: offset)
                //关闭按钮
                Button(action: {
                    self.isShowDetailView = false
                }) {
                    Image(systemName: "xmark.circle.fill")
                        .font(.system(size: 30))
                        .foregroundColor(.black)
                        .opacity(0.7)
                        .contentShape(Rectangle())
                }
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topTrailing)
                .padding(.trailing)
            }
        }
    }
}

快来动手试试吧!


相关文章
|
程序员 索引
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
1076 0
SwiftUI极简教程18:SwipeCard卡片滑动效果的使用(上)
|
JavaScript 前端开发
仿百度排列图片预览插件-Simple Lightbox
仿百度排列图片预览插件-Simple Lightbox
59 0
|
存储 索引
SwiftUI极简教程19:SwipeCard卡片滑动效果的使用(下)
SwiftUI极简教程19:SwipeCard卡片滑动效果的使用(下)
674 0
SwiftUI极简教程19:SwipeCard卡片滑动效果的使用(下)
|
JavaScript 前端开发 API
#yyds干货盘点 ant design mobile实现tab滚动效果和闪屏小记
#yyds干货盘点 ant design mobile实现tab滚动效果和闪屏小记
200 0
|
Swift iOS开发
SwiftUI极简教程01:搭建一个新项目&Text文字的使用
SwiftUI极简教程01:搭建一个新项目&Text文字的使用
608 1
SwiftUI极简教程01:搭建一个新项目&Text文字的使用
|
存储 索引
SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单
在本章中,你将学会使用MatchedGeometryEffect构建一个导航菜单。 在构建SwiftUI应用过程中,我们常常会使用TabView构建底部菜单,但更多的时候会由于我们定制化的需求,需要我们自己绘制底部菜单。 那么本章中,我们就来试试构建一个底部导航菜单。
458 0
SwiftUI极简教程42:使用MatchedGeometryEffect构建一个导航菜单
|
存储
SwiftUI极简教程40:构建SearchBar搜索栏和TabView底部导航
在本章中,你将学会构建Search搜索进行列表搜索和TabView底部导航。 在上一章节中,我们完成了一个简单的ColourAtla色卡App,接下来我们继续完善App的相关内容。
775 0
SwiftUI极简教程40:构建SearchBar搜索栏和TabView底部导航
|
编解码 Swift iOS开发
SwiftUI极简教程24:构建一个Banner图片轮播(上)
SwiftUI极简教程24:构建一个Banner图片轮播(上)
1079 0
SwiftUI极简教程24:构建一个Banner图片轮播(上)
|
索引
SwiftUI极简教程25:构建一个Banner图片轮播(中)
SwiftUI极简教程25:构建一个Banner图片轮播(中)
421 0
SwiftUI极简教程25:构建一个Banner图片轮播(中)
SwiftUI极简教程23:创建一个简单的SearchBar搜索栏
SwiftUI极简教程23:创建一个简单的SearchBar搜索栏
683 0
SwiftUI极简教程23:创建一个简单的SearchBar搜索栏

相关实验场景

更多