4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

フラー株式会社Advent Calendar 2022

Day 13

インスタなどでよく見る重なり合うアイコンをUIStackViewで作ってみた

Posted at

この記事はフラー株式会社 Advent Calendar 2022 13日目の記事です。
12日目は @nnsnodnb さんで「Sign in with Apple のトークン取り消しを実装してみた」でした。

私は現在業務でSNSアプリを作っているので、最近作ったSNS等でよく見るViewを紹介します。

作るもの

ユーザー数によってアイコンの数が変化する、重なり合うアイコンをUIStackViewを用いて作ってみました。

こんな↓感じでいいねしたユーザーのアイコンを最小1つ、最大3つまで表示するやつです。
スクリーンショット 2022-12-13 3.30.53.png スクリーンショット 2022-12-13 2.21.02.png スクリーンショット 2022-12-13 3.31.10.png

ざっくり仕様

  • いいねしたユーザーのアイコンを最小1つ、最大3つまで表示する
  • ユーザーがアイコン画像を設定していない場合は、ローカルのplaceholderイメージを表示する

利用したライブラリ

画像キャッシュライブラリとして Kingfisher を使いました。

いざ実装!

UITableViewCell デザイン

ざっくりcellのデザインを作ります!
今回説明したいのが、Main Icons Stack View以下のところ
スクリーンショット 2022-12-13 3.45.36.png

Point
アイコンの重なりは spacingをマイナス値にすることで実現しました。
また、一番左のアイコンを最上階層にする方法として、semanticをForce Right-to-Left にしました。
スクリーンショット 2022-12-13 4.18.21.png

立体で見るとこういう感じになります。
スクリーンショット 2022-12-13 4.57.50.png

Model

PostとUserの2つのModelを用意します。

import Foundation

struct Post {
    let likedUsers: [User]
}

struct User {
    let id: String
    let icon: URL?
}

UITableViewCell コード

いいねしたユーザー数によって表示するアイコン数の制御や、画像のバインドなどを行います。

import Kingfisher
import UIKit

final class PostTableViewCell: UITableViewCell {
    // MARK: - Properties
    @IBOutlet var likedUsersStackView: UIStackView!
    @IBOutlet var rightIconStackView: UIStackView!
    @IBOutlet var rightIconImageView: UIImageView! {
        didSet {
            rightIconImageView.layer.cornerRadius = rightIconImageView.frame.width / 2
            rightIconImageView.layer.borderWidth = 2
            rightIconImageView.layer.borderColor = UIColor.white.cgColor
        }
    }
    @IBOutlet var centerIconStackView: UIStackView!
    @IBOutlet var centerIconImageView: UIImageView! {
        didSet {
            centerIconImageView.layer.cornerRadius = centerIconImageView.frame.width / 2
            centerIconImageView.layer.borderWidth = 2
            centerIconImageView.layer.borderColor = UIColor.white.cgColor
        }
    }
    @IBOutlet var leftIconImageView: UIImageView! {
        didSet {
            leftIconImageView.layer.cornerRadius = leftIconImageView.frame.width / 2
            leftIconImageView.layer.borderWidth = 2
            leftIconImageView.layer.borderColor = UIColor.white.cgColor
        }
    }
    @IBOutlet var likedUserIDLabel: UILabel! // "travel_lover"部分のラベル
    @IBOutlet var andMoreLabel: UILabel! // "、他"部分のラベル
    
    func configure(post: Post) {
        let users = post.likedUsers
        likedUsersStackView.isHidden = users.isEmpty
        guard !users.isEmpty else { return }
        // ラベル
        likedUserIDLabel.text = users.first?.id
        andMoreLabel.isHidden = users.count < 2
        // アイコンをplaceholderイメージにリセット
        leftIconImageView.image = UIImage(named: "ic_avatar") // 任意のイメージ名
        centerIconImageView.image = UIImage(named: "ic_avatar")
        rightIconImageView.image = UIImage(named: "ic_avatar")
        // IconStackView
        centerIconStackView.isHidden = users.count < 2
        rightIconStackView.isHidden = users.count < 3
        
        // アイコンのURLがある時だけ画像バインド。 users配列の要素を左から順に表示していく。
        users.enumerated().forEach { index, user in
            guard let imageURL = user.icon else { return }
            switch index {
            case 0:
                leftIconImageView.kf.setImage(with: imageURL)
            case 1:
                centerIconImageView.kf.setImage(with: imageURL)
            case 2:
                rightIconImageView.kf.setImage(with: imageURL)
            default:
                return
            }
        }
    }
}

あとは、TableViewControllerを用意し、UITableViewDataSource のcellForRowAtにて上記コードで作成したPostTableViewCellのconfigureを呼びます。
configureの引数のPost型には、Mockデータを渡して動作確認しました。

Mock

Mockは、いいねしたユーザー数や、ユーザーのアイコン設定状況が異なる計6件のデータを用意しました。

        // Icon
        let iconURL1 = URL(string: "https://www.jpeg") // 任意の画像URL
        let iconURL2 = URL(string: "https://xxx.jpeg")
        let iconURL3 = URL(string: "https://yyy.jpeg")
        let iconURL4 = URL(string: "https://zzz.jpeg")
        // User
        let user1 = User(id: "piyopiyo_bowwow", icon: iconURL1) // 任意のIDと画像
        let user2 = User(id: "akai_shikaku", icon: iconURL2)
        let user3 = User(id: "fal_con_1213", icon: iconURL3)
        let user4 = User(id: "big_sankaku", icon: iconURL4)
        let user5 = User(id: "unkown", icon: nil)
        // Post
        let post1 = Post(likedUsers: [user1])
        let post2 = Post(likedUsers: [user2, user1])
        let post3 = Post(likedUsers: [user3, user2, user1])
        let post4 = Post(likedUsers: [])
        let post5 = Post(likedUsers: [user5, user4])
        let post6 = Post(likedUsers: [user4, user3, user2, user1])
        posts = [post1, post2, post3, post4, post5, post6]

できたもの

実行した結果はこんな感じです。
MockのlikedUsersの件数によってアイコンの表示数が変化するViewができました🙌

最後に

普段何気なく見ているSNSですが、細かいところまで凝っていたりして、これどうやって作るんだろう?と思うことばかりです。UI組むのは好きなので色々試して少しづつ解明していきたいですね!

Credit

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?