この記事はフラー株式会社 Advent Calendar 2022 13日目の記事です。
12日目は @nnsnodnb さんで「Sign in with Apple のトークン取り消しを実装してみた」でした。
私は現在業務でSNSアプリを作っているので、最近作ったSNS等でよく見るViewを紹介します。
作るもの
ユーザー数によってアイコンの数が変化する、重なり合うアイコンをUIStackViewを用いて作ってみました。
こんな↓感じでいいねしたユーザーのアイコンを最小1つ、最大3つまで表示するやつです。
ざっくり仕様
- いいねしたユーザーのアイコンを最小1つ、最大3つまで表示する
- ユーザーがアイコン画像を設定していない場合は、ローカルのplaceholderイメージを表示する
利用したライブラリ
画像キャッシュライブラリとして Kingfisher
を使いました。
いざ実装!
UITableViewCell デザイン
ざっくりcellのデザインを作ります!
今回説明したいのが、Main Icons Stack View
以下のところ
Point
アイコンの重なりは spacingをマイナス値にすることで実現しました。
また、一番左のアイコンを最上階層にする方法として、semanticをForce Right-to-Left
にしました。
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
- hart icon
Favorite icon by Icons8 - icon placeholder image
Image by WingTillDie from Pixabay