尝试用AI辅佐我写一个类抖音的APP(一)中想让AI帮我写一个完好的APP,不现实。那么接下来,还是一步一步来吧。一口吃不成一个胖子,这点在AI里也是成立的。
探究中心部分,上下滑动翻页
最中心的功用便是滑动翻页了,所以,持续探究。上一篇尝试用AI辅佐我写一个类抖音的APP(一)中Claude的答复相对靠谱点,接着问Claude:
上面的代码漏了这个功用 1.向上滑动翻页,每页一个皮肤
Claude答:
您提醒得对,我漏掉了翻页功用。能够经过以下办法完成:
运用@State变量记载当时显现的皮肤索引currentPage
用TabView包住Skin3DView,在TabView里遍历skins数组展现每个皮肤
给TabView增加.tabViewStyle(.page(indexDisplayMode: .never))使其支撑滑动翻页
在TabView上增加.gesture(DragGesture().onChanged{当时页码 += delta.translation.height / UIScreen.main.bounds.height})来经过拖拽手势更新currentPage
修正后的代码如下:
struct SkinView: View {
@State private var skins = [Skin]()
@State var currentPage = 0
var body: some View {
TabView(selection: $currentPage) {
ForEach(skins, id: \.self) { skin in
Skin3DView(skin: skin)
.rotationGesture(for: .horizontal)
.disabled(for: .vertical)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
.gesture(
DragGesture()
.onChanged {
self.currentPage -= Int($0.translation.height / UIScreen.main.bounds.height)
}
)
// 其他代码不变
}
}
这样就能够经过上下拖拽TabView来完成翻页了。请检查是否契合您的要求,需求我解说或修正的地方请指出,谢谢!
问ChatGPT,给出的翻页完成同样是TabView办法。ChatGPT的上下文见尝试用AI辅佐我写一个类抖音的APP(一) 问:
这部分完成滑动翻页,不能做到每次翻一整页,请修正完善代码。‘’‘struct ContentView: View { @StateObject var skinViewModel = SkinViewModel() var body: some View { VStack { ScrollView { ForEach(skinViewModel.skins, id: .self) { skin in SkinView(skin: skin) } } AdView() } .onAppear { skinViewModel.fetchSkins() } } }’‘’
答:
关于完成滑动翻页(每次滑动显现一个新的页面),咱们能够运用 TabView 代替 ScrollView。每个 SkinView 能够作为一个独立的页面。在 TabView 的 tabViewStyle 参数中,运用 PageTabViewStyle 来完成页面翻页的作用。
以下是修正后的 ContentView:
struct ContentView: View {
@StateObject var skinViewModel = SkinViewModel()
var body: some View {
VStack {
TabView {
ForEach(skinViewModel.skins, id: \.self) { skin in
SkinView(skin: skin)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
AdView()
}
.onAppear {
skinViewModel.fetchSkins()
}
}
}
这样,每次滑动都会显现一个新的 SkinView 页面,完成了翻页作用。
留意,PageTabViewStyle 默认会显现一个页面指示器(小点),假如你不想显现它,能够设置 indexDisplayMode 为 .never。
请留意,上述代码仅为示例,或许需求依据实践项目需求进行调整。
上面的两个答案,都要用TabView,但TabView只支撑水平滑动,假如要笔直滑动,那么就要将页面旋转90度。有没有不必TabView完成的计划呢?问了ChatGPT,它给出了一个计划。 问:
swiftUI完成相似抖音的上滑翻页,每次滑动一整页,不必TabView完成,用Scrollview完成能够吗?给出完好代码
ChatGPT答:
在SwiftUI中,能够运用ScrollView和手势识别器完成相似抖音的上滑翻页作用,不过需求留意的是,ScrollView并不直接支撑分页作用,需求自己去做一些调整。 以下是一个简略的比方:
import SwiftUI
struct ContentView: View {
@State private var currentPage = 0
let totalPage = 5
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 0) {
ForEach(0..<self.totalPage) { i in
PageView(pageNumber: i)
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
.content.offset(y: -CGFloat(self.currentPage) * geometry.size.height)
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .leading)
.gesture(
DragGesture()
.onEnded { value in
let offset = value.translation.height / geometry.size.height
let newIndex = (CGFloat(self.currentPage) - offset).rounded()
self.currentPage = min(max(Int(newIndex), 0), self.totalPage - 1)
}
)
}
}
}
struct PageView: View {
var pageNumber: Int
var body: some View {
VStack {
Text("Page \(pageNumber)")
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.random)
.edgesIgnoringSafeArea(.all)
}
}
// 为了让每个页面的色彩不同,咱们能够经过扩展Color来生成随机色彩
extension Color {
static var random: Color {
return Color(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1))
}
}
在这个比方中,一个ScrollView包含了多个PageView,每个PageView占据了整个ScrollView的宽度和高度。当用户滑动ScrollView时,咱们会依据滑动的间隔来计算当时显现的页面,然后修正ScrollView的offset来显现正确的页面。
留意:这个比方在iOS 13和14上运转杰出,但在15上或许会有一些问题,因为在15中,ScrollView的行为有所改动。
选一个,用TabView来完成看看有什么问题
结合上面的代码,快速写了一个上滑翻页的代码
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
//写一个抖音相同的滑动视图
TabView {
ForEach(0..<10) { _ in
// Image("tikmc")
Image(systemName: "globe")
.resizable()
.scaledToFill()
.frame(width: 100, height: 100)
.clipped()
.rotationEffect(.degrees(-90))
}
}.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.rotationEffect(.degrees(90))
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
作用如下
能够看到, 这个滑动翻页是能够完成的,但有个问题是,滑动快的时分出现一下滑动好几页的状况。活动太快出现多页的状况试了下在真机上测试,倒是不太明显。
问题2: TabView旋转90度后,显现的区域十分小,TabView的高度被限制手机屏幕宽度,所以看起来是个正方形。见下图
刚好昨天请求下来了Github Copilot Chat,试了一下它给的代码,问题更多。
TabView(selection: $currentPage) {
ForEach(0..<100) { index in
Skin3DView()
.frame(width: 1000, height: 1000)
.rotationEffect(.degrees(-90))
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
.onChanged({ value in
if value.translation.width > 0 {
if currentPage > 0 {
currentPage -= 1
}
} else {
// if currentPage < skins.count - 1 {
currentPage += 1
// }
}
}))
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.rotationEffect(.degrees(90))
.frame(height: 1000)
这段代码滑动用滑动间隔>0来翻页,页面切得飞快。不禁感叹,Github Copilot智能程度有限唉。
那么尝试用Scrollview直接完成上下翻页吧
这次用让AI给出答案都不可
用ScrollView完成一个模仿抖音的上下滑动翻页的swiftUI代码,要求: (1)不能用TabView,作用欠好 (2)必须要用ScrollView(3)每次只能滑动一页
ChatGPT答复
在SwiftUI中,咱们能够运用ScrollView来完成相似抖音的上下翻页作用。不过,咱们需求一些额外的处理来确保每次只滑动一页,而且ScrollView没有内建的页面控制机制,所以咱们需求手动处理滑动事件。
下面是运用ScrollView来完成的一个比方:
import SwiftUI
struct ContentView: View {
@State private var currentPage: Int = 0
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 0) {
ForEach(0..<10) { i in
Image("image\(i)")
.resizable()
.scaledToFit()
.frame(width: geometry.size.width, height: geometry.size.height)
.overlay(Color.black.opacity(0.4))
}
}
}
.content.offset(y: -geometry.size.height * CGFloat(currentPage))
.frame(width: geometry.size.width, height: nil, alignment: .top)
.gesture(
DragGesture().onEnded { value in
if value.predictedEndTranslation.height > 50, currentPage > 0 {
currentPage -= 1
} else if value.predictedEndTranslation.height < -50, currentPage < 9 {
currentPage += 1
}
}
)
}
}
}
这个比方中,咱们运用了一个ScrollView而且把它的content.offset设为了根据当时页面的偏移量。然后咱们增加了一个DragGesture,当用户完毕拖动时,咱们会检查predictedEndTranslation来决议是否翻页。
这个比方的一个限制是一切的图片都必须在一开始就加载,假如有大量的图片,或许会消耗大量内存。关于这种状况,或许需求运用其他的办法来完成,比方自定义的ScrollView。
另外,这个比方只是一个基本的完成,或许还需求处理其他的细节,比方滑动动画、页面指示器等。
Claude的答案也不可,这个只能自己修理了。
体验了一下抖音,抖音加载视频的时分,是从第一页开始的,在第一页下拉,会触发它的刷新。所以估测它每次是加载一个视频列表,每个视频是一个分页。 这时分能够用LazyVStack来显现每个页面,页面的高度为除了屏幕高度减去下面的tab栏高度。
一开始我自己整了一个根底的代码,是这样
struct ContentView: View {
@State private var currentPage: Int = 0
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 0) {
ForEach(0..<10) { i in
Skin3DView()
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
}.edgesIgnoringSafeArea(.all)//这行代码是问了Gpt后它加上去的
}
}
补充问ChatGPT的问题:
修正下面的代码,完成Skin3DView铺满整个屏幕,疏忽屏幕安全区域。
struct ContentView: View {
@State private var currentPage: Int = 0
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 0) {
ForEach(0..<10) { i in
Skin3DView()
}
}
}
}
}
}
好了,这时分能够完成页面占满屏幕,并能够滑动了。但不是抖音的那样每次滑动一页后就中止,这个是能够滑很多页。
进一步改善,让它每次只能滑动一页
在做这一步让ChatGPT给出相似抖音的滑动翻页的时分,它给出的答案都不可行。想起提问时设定ChatGPT的身份很重要,所以先对ChatGPT吹捧一番,说它是一名牢靠的抖音开发,这样就相对限定了它尽或许使用抖音开发人员发的语料。这一招公然好使,下面给出的这个答案靠谱多了
你是一名牢靠的抖音开发,先了解抖音的滑动翻页交互,再给上面面这段代码增加如下功用:(1)滑动间隔假如超越屏幕高度的20%就滑动完一整页 (2)滑动完一整页就中止滑动
这个需求能够经过运用SwiftUI的ScrollView和DragGesture合作运用来完成。以下是一个简略的完成:
struct ContentView: View {
@State private var currentPage = 0
@GestureState private var translation: CGFloat = 0
var body: some View {
GeometryReader { geometry in
let screenHeight = geometry.size.height
let threshold = screenHeight * 0.2
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 0) {
ForEach(0..<10) { i in
Skin3DView()
.frame(height: screenHeight)
.rotationEffect(.degrees(Double(self.translation / 5))) // Add rotation effect while dragging
.offset(y: self.translation)
.animation(.interactiveSpring(), value: self.translation)
}
}.frame(height: screenHeight)
}
.content.offset(y: -CGFloat(self.currentPage) * screenHeight)
.offset(y: self.translation)
.animation(.interactiveSpring(), value: self.translation)
.gesture(
DragGesture()
.updating(self.$translation) { value, state, _ in
state = value.translation.height
}
.onEnded { value in
let offset = value.translation.height
if abs(offset) > threshold {
self.currentPage += (offset < 0 ? 1 : -1)
}
}
)
.edgesIgnoringSafeArea(.all)
}
}
}
这儿咱们运用了DragGesture手势,经过更新translation状况,完成了滑动时的交互动画。在手势完毕时,咱们依据滑动的间隔判断是否翻页,并更新currentPage。经过对ScrollView的content.offset进行调整,完成了滑动翻页的作用。
这是一个简略的完成,或许需求依据实践的需求进行调整。比方,你或许需求处理滑动方向,或者增加更杂乱的交互动画等。
上面的答案美中不足的便是运转时页面没有停在鸿沟上,两页之间的分界在屏幕大约中心位置。还有个问题便是没有对第一页和最终一页做越界处理。
上面的答案相对靠谱,那么就根据这个上面修正一下,应该能够完成。所以,对代码中有些不太清楚的细节,问了ChatGPT。目的是想了解代码在处理拖拽过程中做了什么。
解说一下这个代码 DragGesture() .updating(self.$translation) { value, state, _ in state = value.translation.height }
这段代码是SwiftUI中的拖拽手势(DragGesture)的运用。
首要,DragGesture()创建了一个拖拽手势。
.updating(self.$translation) { value, state, _ in state = value.translation.height }这行代码做了以下几件事:
这儿用到了SwiftUI的@GestureState特点包装器,它能够存储手势的暂时状况。在这个比方中,translation便是存储手势的暂时状况。
.updating(:_:_:)办法会在手势发生改动时被调用,它有三个参数:
第一个参数是一个绑定(Binding),在这个比方中是self.$translation。它指向了保存手势状况的变量。
第二个参数是一个闭包,会在手势发生改动时被调用。闭包中有三个参数:
value:表明手势的当时值。在这个比方中,value.translation.height表明手势在笔直方向上的偏移量。
state:表明手势的暂时状况。在闭包内部,咱们将手势的笔直偏移量赋值给了state,这样translation就被更新了。
最终一个参数是一个Transaction,它包含了手势改动的详细信息,比方时间和动画。在这个比方中,咱们不需求这个参数,所以用_疏忽了它。
总的来说,这段代码的作用是:在拖拽手势发生变化时,将手势在笔直方向上的偏移量存储到translation中。
ChatGPT这个解说很精确了,便是@GestureState private var translation: CGFloat = 0这个变量是用来获取滑动的位移的,updating中的state变量要用参数value来更新位移,这样才干传给translation。
有了实时的位移信息,便能够经过改动content offset来改动scrollview的位移,见代码 .content.offset(y: -CGFloat(self.currentPage) * screenHeight),其间translation影响了currentPage然后影响了content offset。
因为currentPage不是一个连续的值,那么滑动前后会导致content offset的跳变,要想实时连续地看到滑动的作用,代码中增加了 .offset(y: self.translation)这一行。translation在滑动时有偏移,手指松开滑动完毕onEnded处理完后便为0了,所以不影响页面的偏移。在onEnded里更新了currentPage,content offset也有了正确的值。
了解了代码思路后,咱们就能够针对性地修正了。中心是 content offset的改动, 外加过程动画。过程动画整个scrollview有一个offset就能够了, 把Skin3DView里的offset和动画删去。经过调试发现.edgesIgnoringSafeArea(.all)要放在GeometryReader这一层才干疏忽屏幕安全区域,其实它的上面一次答复的代码是正确的,这次却放错地方了。另外便是LazyVStack不必指定高度,删去它的.frame(height: screenHeight)。
再次运转,十分完美。
附上代码
struct ContentView: View {
@State private var currentPage = 0
@GestureState private var translation: CGFloat = 0
var body: some View {
GeometryReader { geometry in
let screenHeight = geometry.size.height
let threshold = screenHeight * 0.1 //这儿把0.2改成0.1更顺畅了
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 0) {
ForEach(0..<10) { i in
Skin3DView()
.frame(height: screenHeight)
// .rotationEffect(.degrees(Double(self.translation / 5))) // Add rotation effect while dragging
// .offset(y: self.translation)
// .animation(.interactiveSpring(), value: self.translation)
}
}//.frame(height: screenHeight)
}
.content.offset(y: -CGFloat(self.currentPage) * screenHeight)
.offset(y: self.translation)
.animation(.interactiveSpring(), value: self.translation)
// .animation(.interactiveSpring(), value: self.currentPage)
.gesture(
DragGesture()
.updating(self.$translation) { value, state, _ in
state = value.translation.height
}
.onEnded { value in
let offset = value.translation.height
if abs(offset) > threshold {
if offset < 0 && self.currentPage < 9 {//上滑,翻下一页
self.currentPage += 1
}else if offset > 0 && self.currentPage > 0{
self.currentPage -= 1
}
}
}
)
}.edgesIgnoringSafeArea(.all)
}
}
总结
ChatGPT4在答复问题的精确程度上,的确是比Claude要好。内容的新旧程度上也比Claude要新,例如Claude的答案没有用SwiftUI的SceneView,还是用包装的UIKit中的SceneKit,但ChatGPT4有。 另外,要用好AI,给它一个人设很重要,我了解是能够大约圈定它的语料规模,不会满世界给你找一个好像相关的答案。