【SwiftUI】Likeボタンとリスト内セルにボタンを実装する場合の注意点


はじめに

SwiftUIでFirebaseからデータを読み取り、これまで作成したカード内容を更新することを目的とする。

今回はカード内のLikeボタンの実装を行います。

結論を最初に言うと、ListView内でButtonを実装する場合はそれぞれのImageに.onTapGestureを設定する必要があります。
その点がわかっていなかったため、実装に時間がかかってしまいました。

前回までの記事は以下を参考ください。

参考記事
【SwiftUI】Firebaseからデータを読み取り、ListViewのRowを更新する

開発環境

OSX 10.15.7 (Catalina)
Xcode 12.2.0
CocoaPods 1.10.0

Like機能の実装

ボタンの設定に時間がかかってしまったため、FirebaseのLike数の更新機能の実装までは至りませんでした。
今回はタップした際にハートの色を変更する機能のみ実装します。
設定のみしたaction機能は次回以降に実装します。

実装方法

struct ContentView: View {

    @State private var isLiked = false

    var body: some View {
        VStack {
            Text(isLiked ? "Liked!" : "unLiked!")
            HeartButton(isLiked: $isLiked)
        }
    }
}

struct HeartButton: View {
    @Binding var isLiked : Bool


    var body: some View {
        Button(action: {
            self.isLiked.toggle()
        }, label: {
            Image(systemName: isLiked ? "heart.fill": "heart")
                .font(.largeTitle)
                .foregroundColor(Color.red)
                .frame(width: 100, height: 100)
        })
    }
}

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

タップ前

タップ後

コード全体

今回機能の実装にあたり、View内のコードを見直し、修正しました。
上記のコードを参考に、List内のカードに実装します。
List内にButtonを実装する場合は、実装した場合はセル全体が選択可能になってしまい、特定のボタンのみを選択することができません。
そのため、ボタンの代わりにtextやimageに.onTapGestureを実装する必要があります。
現在は色が変わるだけしか実装できていません。
今後はタップした場合にFirebaseを更新し、Like数が増減できるようにする予定です。

struct ContentRowView: View {
    var id  = ""
    var user = ""
    var userImage = ""
    var haiku = ""
    var mapImage = ""
    var place = ""
    var likes = ""
    // ○秒、○日、○年前を表示

    var createdDate = ""

    static let formatter = RelativeDateTimeFormatter()

    // 以下を追記
    @State var isLiked = false

    var body: some View {

        ContentRowView.formatter.locale = Locale(identifier: "ja_JP")

        let fmt = ISO8601DateFormatter()
        let date1 = fmt.date(from: createdDate)!
        let components = Calendar.current.dateComponents(
            [.day, .year, .month, .minute, .second],
            from: Date(),
            to: date1
        )
        let timeAgo = ContentRowView.formatter.localizedString(from: components)

        return VStack {
            // mapImage
            VStack {
                AnimatedImage(url: URL(string: mapImage)!)
                    .resizable()
                    //.aspectRatio(contentMode: .fit)
                    .cornerRadius(12.0, antialiased: /*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
                HStack {
                    Spacer()
                    Text(place).font(.caption).foregroundColor(.gray)
                        .padding(.bottom, 5)
                }
            }
            // Haiku
            Text(haiku).font(.title3).fontWeight(.bold)
            // Likes Number
            HStack {
                Image(systemName: "heart").font(.headline).foregroundColor(Color("pinkColor"))
                Text(likes).font(.headline).foregroundColor(Color("pinkColor"))
                Spacer()
            }.padding(.top, 5)
            // Info
            HStack {
                AnimatedImage(url: URL(string: userImage)!)
                    .resizable()
                    .frame(width: 30, height: 30)
                    .clipShape(Circle())

                Text(user).font(.headline).fontWeight(.light)
                + Text("・").font(.headline).fontWeight(.light)
                + Text(timeAgo).font(.headline).fontWeight(.light)
                Spacer()
                    // If Comment Button was push, it call CommentView.
                Image(systemName: "text.bubble")
                    .font(.title)
                    .foregroundColor(.gray)
                    .onTapGesture {
                        print("Button Tapped")
                    }
                    .frame(width: 30, height: 30)

                // Like Button push Image to add like's Number.
                Image(systemName: isLiked ? "heart.fill": "heart")
                    .font(.title)
                    .foregroundColor(Color("pinkColor"))
                    .onTapGesture {
                        self.isLiked.toggle()
                    }
                    .frame(width: 30, height: 30)
            }.padding(5)
        }.padding(.top, 8).frame(height: 391)
    }
}

追記

エラー
下記のエラーを確認して、修正しようとしましたが、現状では解決できないエラーのようです。
無視しても致命的にはならない現象のため、いったんそのままにします。

nw_protocol_get_quic_image_block_invoke dlopen libquic failed

参考文献
Strange error nw_protocol_get_quic_image_block_invoke dlopen libquic failed
Swift Firebase Connection