SwiftUIミニマリストチュートリアル18:SwipeCardカードのスライド効果の使用(パート1)

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して18日目です。クリックしてイベントの詳細をご覧ください

今日の言葉:コンテンツが重要な場合は、重要なコンテンツを繰り返します。

この章では、GesturesジェスチャーとAnimationsアニメーションを使用してSwipeCardカードをスワイプする方法を学習します。

交友类的Appそれらの多くでは、 " 向左向右滑动卡片"インタラクションがあり、ユーザーは右にスワイプして写真を表示でき、喜欢点赞左にスワイプすると写真ポイントを表示できます不喜欢

283.png

SwipeCardしたがって、この章では、カードのスライド操作効果に似た単純なアプリケーションを作成してみましょう。

さて、始めましょう!

まず、という名前の新しいプロジェクトを作成しますSwiftUISwipeCard

284.png

まずAssets.xcassets、素材として使用する写真のバッチをインポートします。それが一連​​の写真である限り、あなたはいくつかの風景写真、または肖像写真、食べ物の写真を見つけることができます。コード内で画像を見つけて参照しやすくするために、画像の名前を変更することを忘れないでください。

285.png

スワイプ機能を実装する前に、まずメインページを作成しましょう。メインUIページを次の3つの部分に分割します。

1. TopBarMenu上部のナビゲーションバー

  1. CardViewカードビュー

3.BottomBarMenu下部のメニューバー

TopBarMenuトップナビゲーションバー

ここではTopBarMenu、上部のナビゲーションバービューを表示するための新しい構造体ページを作成し、名前を付けますTopBarMenu

コードは次のように表示されます。

//顶部导航栏
struct TopBarMenu: View {
    var body: some View {
        HStack {
            Image(systemName: "ellipsis.circle")
                .font(.system(size: 30))

            Spacer()

            Image(systemName: "heart.circle")
                .font(.system(size: 30))
        }.padding()
    }
}
复制代码

ここではトップナビゲーションバーを使用していません.NavigationView。多くの場合、ナビゲーションバーには多くのカスタマイズされた機能が必要であり.NavigationView、トップナビゲーションバーで実際のビジネスをサポートするのは難しい場合があるため、“成熟的”プログラマーは独自のトップナビゲーションを作成するのが好きです。バースタイル。

上面我们做的TopBarMenu顶部导航栏很简单,就2张图片,使用横向HStack排布。然后我们在CardView里引用TopBarMenu顶部导航栏视图,效果如下:

286.png

CardView卡片视图

接着,我们创建一个新的结构体页面来展示卡片视图,命名为CardView

代码如下:

//卡片视图
struct CardView: View {
    var body: some View {
        Image("image01")
            .resizable()
            .frame(minWidth: 0, maxWidth: .infinity)
            .cornerRadius(10)
            .padding(.horizontal, 15)

            .overlay(
                VStack {
                    Text("图片01")
                        .font(.system(.headline, design: .rounded)).fontWeight(.bold)
                        .padding(.horizontal, 30)
                        .padding(.vertical, 10)
                        .background(Color.white)
                        .cornerRadius(5)
                }
                .padding([.bottom], 20), alignment: .bottom
            )
    }
}
复制代码

CardView卡片视图也非常简单,我们放在一个Image图片,让将一个Text文字“悬浮”在图片底部。我们在CardView里引用CardView卡片视图,由于CardView卡片视图和TopBarMenu顶部导航栏是纵向排列,我们使用VStack包裹住。

struct ContentView: View {
    var body: some View {
        VStack {
            TopBarMenu()
            CardView()
        }
    }
}
复制代码

287.png

BottomBarMenu底部菜单栏

底部导航栏也是如此,我们创建一个新的结构体页面叫做BottomBarMenu。

代码如下:

// 底部导航栏
struct BottomBarMenu: View {
    var body: some View {

        HStack {

            Image(systemName: "xmark")
                .font(.system(size: 30))
                .foregroundColor(.black)

            Button(action: {

            }) {

                Text("立即选择")
                    .font(.system(.subheadline, design: .rounded)).bold()
                    .foregroundColor(.white)
                    .padding(.horizontal, 35)
                    .padding(.vertical, 15)
                    .background(Color.black)
                    .cornerRadius(10)
            }.padding(.horizontal, 20)

            Image(systemName: "heart")
                .font(.system(size: 30))
                .foregroundColor(.black)
        }
    }
}
复制代码

BottomBarMenu底部导航栏也是我们自己写的,使用3个元素,2个Image图片,1个Text文字按钮。然后也在ContentView主要页面中展示它,效果如下:

288.png

我们进一步美化下样式,使用Spacer()分开CardView卡片视图和BottomBarMenu底部导航栏视图,我们保持最小20的区域,就得到了下面的效果。

struct ContentView: View {
    var body: some View {

        VStack {

            TopBarMenu()
            CardView()

            Spacer(minLength: 20)

            BottomBarMenu()
        }
    }
}
复制代码

289.png

好了,基础的样式我们做完了。

交互逻辑分析

接下来,可以实现SwipeCard卡片滑动的效果了。先解释一下SwipeCard卡片滑动的原理,你可以它想象成一组叠在一起的卡片,每张卡片都显示一张照片。

我们将最上面的那张卡,即第一张图片,稍微向左或向右刷一下,就会打开下面的下一张卡片,也就是第二张图片

如果你放开卡片,卡片会回到原来的位置。但如果你用力滑动图片卡片,就可以将图片卡片“丢掉”,系统就会将把第二张图片向前拉变成最上面的图片展示。

我们了解了原理后,我们先实现CardView卡片部分的内容。这里使用ZStack将一堆卡片“堆在”一起,而图片卡片的遍历方式之前的章节已经学过。

//创建Album定义变量
struct Album: Identifiable {
    var id = UUID()
    var name: String
    var image: String
}

//创建演示数据
var album = [
    Album(name: "图片01", image: "image01"),
    Album(name: "图片02", image: "image02"),
    Album(name: "图片03", image: "image03"),
    Album(name: "图片04", image: "image04"),
    Album(name: "图片05", image: "image05"),
    Album(name: "图片06", image: "image06"),
    Album(name: "图片07", image: "image07"),
    Album(name: "图片08", image: "image08"),
    Album(name: "图片09", image: "image09")
]
复制代码

由于我们之前定义的CardView卡片视图中使用的是Image图片和Text文字。

这里我们定义两个常量替换它,这样我们就可以在ContentView主视图定义的值了。

let name: String
let image: String
复制代码

290.png

然后,我们在ContentView主视图使用ZStack包裹CardView卡片视图,再使用ForEach循环遍历album数组所有数据。

//卡片视图
ZStack {
    ForEach(album) { album in
        CardView(name: album.name, image: album.image)
    }
}
复制代码

291.png

我们发现,模拟器突然换了一张图片,这是因为我们定义的album图片数组,使用ForEach循环时是一张张遍历的,最后遍历完是album图片数组最后一张图片。

ForEach循环中,第一张图片放在了最底下。因此,最后一张图片也就成了最上面的照片。

因此,虽然我们实现了album图片数组的遍历,但还是存在两个问题:

1、本该是第一张图片,现在变成了最后一张。

2、现在我们只有9张图片卡片,但如果之后我们有更多的图片卡片的时候,我们是否应该为每张图片创建一个卡片视图?

album数组图片排序问题

我们一个个解决,首先第一个问题,卡片顺序的问题。好在SwiftUI提供了zIndex修饰符来来确定ZStack中视图的顺序,zIndex值越高,视图层级也就越高。

zIndexカードビューの値を取得するメソッドを作成します。

//获得图片zIndex值

func isTopCard(cardView: Album) -> Bool {
    guard let index = album.firstIndex(where: { $0.id == cardView.id }) else {
        return false
            }
    return index == 0
}
复制代码

上記のメソッド関数は、カードビューを取得してそのインデックスを検索し、このカードビューが一番上にあるかどうかを示します。

次に、CardViewカードビューでこのメソッドを参照します。

.zIndex(self.isTopCard(cardView: album) ? 1 : 0)
复制代码

各カードビューに修飾子を追加し、一番上のカードにzIndexを割り当てます更高zIndex

それで、最初の写真を顶部卡片ディスプレイとして取得しました。

292.png

階層の問題を表示する

次に、を解き第二个问题ます。

将来、カードの数が無限になると、ビューを無限に作成するのは明らかに非現実的ですが、他の方法を考えてもらえますか?

方法も非常に簡単です。実際、考えてみると2个卡片视图もう1つスライドして最初のビューに戻るだけですが、表示される画像は異なります滑动一个显示另一个卡片视图このように、いくつの写真でも、必要な2个卡片视图来回切换効果を達成するだけで済みます。

早くやれよ。

原則として、多くの画像構造を初期化する必要はなく、最初の2つだけが必要です。最初のカードビューが破棄されたら、2番目のカードビューを追加します。

//创建2个卡片视图
var albums: [Album] = {

    var views = [Album]()

    for index in 0..<2 {
        views.append(Album(name: album[index].name, image: album[index].image))
    }
    return views
}()
复制代码

293.png

新しい配列を定義したので、画像値を取得する方法で、パラメータによって読み取られた値を判定配列に置き換えることをalbums忘れないでください。zIndexindexalbums

このようにして、2つのビューのみを作成し、画像配列のトラバーサル表示を完了します。

完全なコードは次のとおりです。

import SwiftUI

//创建Album定义变量
struct Album: Identifiable {
    var id = UUID()
    var name: String
    var image: String

}

//创建演示数据
var album = [
    Album(name: "图片01", image: "image01"),
    Album(name: "图片02", image: "image02"),
    Album(name: "图片03", image: "image03"),
    Album(name: "图片04", image: "image04"),
    Album(name: "图片05", image: "image05"),
    Album(name: "图片06", image: "image06"),
    Album(name: "图片07", image: "image07"),
    Album(name: "图片08", image: "image08"),
    Album(name: "图片09", image: "image09")

]

//创建2个卡片视图
var albums: [Album] = {

    var views = [Album]()

    for index in 0..<2 {
        views.append(Album(name: album[index].name, image: album[index].image))
    }
    return views
}()

struct ContentView: View {
    var body: some View {

        VStack {

            //顶部导航栏
            TopBarMenu()

            //卡片视图
            ZStack {
                ForEach(albums) { album in
                    CardView(name: album.name, image: album.image)
                        .zIndex(self.isTopCard(cardView: album) ? 1 : 0)
                }
            }

            Spacer(minLength: 20)

            //底部导航栏
            BottomBarMenu()
        }
    }

    //获得图片zIndex值
    func isTopCard(cardView: Album) -> Bool {
        guard let index = albums.firstIndex(where: { $0.id == cardView.id }) else {
            return false
        }
        return index == 0
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

// 顶部导航栏
struct TopBarMenu: View {
    var body: some View {
    
        HStack {

            Image(systemName: "ellipsis.circle")
                .font(.system(size: 30))

            Spacer()
            
            Image(systemName: "heart.circle")
                .font(.system(size: 30))
        }.padding()
    }
}

//卡片视图
struct CardView: View {

    let name: String
    let image: String

    var body: some View {

        Image(image)
            .resizable()
            .frame(minWidth: 0, maxWidth: .infinity)
            .cornerRadius(10)
            .padding(.horizontal, 15)

            .overlay(
                VStack {
                    Text(name)
                        .font(.system(.headline, design: .rounded)).fontWeight(.bold)
                        .padding(.horizontal, 30)
                        .padding(.vertical, 10)
                        .background(Color.white)
                        .cornerRadius(5)
                }
                .padding([.bottom], 20), alignment: .bottom
            )
    }
}

// 底部导航栏
struct BottomBarMenu: View {
    var body: some View {

        HStack {
            Image(systemName: "xmark")
                .font(.system(size: 30))
                .foregroundColor(.black)

            Button(action: {

            }) {
                Text("立即选择")
                    .font(.system(.subheadline, design: .rounded)).bold()
                    .foregroundColor(.white)
                    .padding(.horizontal, 35)
                    .padding(.vertical, 15)
                    .background(Color.black)
                    .cornerRadius(10)
            }.padding(.horizontal, 20)

            Image(systemName: "heart")
                .font(.system(size: 30))
                .foregroundColor(.black)
        }
    }
}
复制代码

つづく

SwipeCardカードのスライド効果は内容が多すぎるため、消化を助けるために2つの章に分かれています

SwipeCard卡片滑动效果的使用(上)の部分では、基本的なスタイルといくつかの準備作業のみが完了しています。

是非、お試しください!

このコラムがお役に立てば、いいね、コメント、フォローしてください〜

おすすめ

転載: juejin.im/post/7087760928883605518