2
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.

UICollectionViewでドット絵表示

Posted at

Xcode-13.1

はじめに

WWDC 2019 で紹介された UICollectionView でシュバババと並べ替えが起きるアニメーションをみてやってみたい!と思った人は多いと思います(Advances in UI Data Sourcesの 22:00 ~ くらいのやつ)。

ドット絵表示して切り替えるときに使うとかっこいいんじゃないかと思いこういうのを作ってみました。

ドット絵表示

まず UICollectionView を使って 32 * 32 のキャンバスを作ってそこにドット絵を表示します。

Compositional Layouts と Diffable Data Sources どちらもあまり理解してないので下記記事参考にさせていただきました。

キャンバス作成

下記のように 32 * 32 のキャンバスを作成します(ドット打つの大変だったので 16 * 16 のがいいかもです)。

private enum Section {
    case main
}

/// サイズは256*256にしておく
@IBOutlet private weak var collectionView: UICollectionView!
private var dataSource: UICollectionViewDiffableDataSource<Section, Int>!

private func makeSection() -> NSCollectionLayoutSection {
    let itemCount = 32 // 横に並べる数
    let itemSpacing: CGFloat = 0 // セル間のスペース
    let dotSize: CGFloat = 8 // ドットのサイズ

    let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(dotSize),
                                                        heightDimension: .absolute(dotSize)))
    let items = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1.0),
                                                                        heightDimension: .fractionalHeight(1.0)),
                                                    subitem: item,
                                                    count: itemCount)
    items.interItemSpacing = .fixed(itemSpacing)
    let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: .fractionalWidth(1.0),
                                                                    heightDimension: .absolute(dotSize)),
                                                    subitems: [items])

    let section = NSCollectionLayoutSection(group: group)
    return section
}

collectionView.collectionViewLayout = UICollectionViewCompositionalLayout(section: makeSection())
dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) {
    (collectionView: UICollectionView, indexPath: IndexPath, item: Int) -> UICollectionViewCell? in
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
    cell.backgroundColor = .systemGreen
    return cell
}

let items = (0...1023).map { Int($0) }
var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
snapshot.appendSections([.main])
snapshot.appendItems(items, toSection: .main)
dataSource.apply(snapshot, animatingDifferences: false)

こんな感じです。

dot1

ドット絵表示

使う色の文だけ Dot を定義します。

enum Dot {
    case chocolate1
    case chocolate2
    case cracker1
    case cracker2
    case outline
    case background

    var color: UIColor {
        switch self {
        case .chocolate1:
            return .init(red: 93/255, green: 63/255, blue: 48/255, alpha: 1.0)
        case .chocolate2:
            return .init(red: 60/255, green: 42/255, blue: 33/255, alpha: 1.0)
        case .cracker1:
            return .init(red: 236/255, green: 193/255, blue: 131/255, alpha: 1.0)
        case .cracker2:
            return .init(red: 164/255, green: 137/255, blue: 97/255, alpha: 1.0)
        case .outline:
            return .init(red: 96/255, green: 96/255, blue: 96/255, alpha: 1.0)
        case .background:
            return .systemGreen
        }
    }
}

こんな感じで 1024 の配列でドット絵を定義します。

let mushroom: [Dot] = [
    .background, .background, .background, .background, .background, .background, .background, .background,
    .background, .background, .background, .background, .outline, .outline, .outline, .outline,
    .outline, .outline, .outline, .outline, .background, .background, .background, .background,
    .background, .background, .background, .background, .background, .background, .background, .background,

    :
    :
    :
]

あとは下記のようにしてドット絵を表示するだけです!

private var dataList: [Dot] = mushroom

dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) {
    (collectionView: UICollectionView, indexPath: IndexPath, item: Int) -> UICollectionViewCell? in
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
    cell.backgroundColor = self.dataList[item].color
    return cell
}

こんな感じです。

dot2

ドット絵切り替え

ドット絵が表示できたのであとは複数のドット絵の切り替え処理です。

もう1つドット絵データを作成します。

let bambooshoot: [Dot] = [
    .background, .background, .background, .background, .background, .background, .background, .background,
    .background, .background, .background, .background, .background, .background, .background, .outline,
    .outline, .background, .background, .background, .background, .background, .background, .background,
    .background, .background, .background, .background, .background, .background, .background, .background,
    :
    :
    :
]

切り替え用フラグを用意してデータ生成の処理を修正します。

private var mode = 0

let items = (0...mushroom.count-1).map { Int($0) }
dataList = .init(repeating: .background, count: items.count)
items.forEach { item in
    switch mode {
    case 1:
        dataList[item] = bambooshoot[item]
    case 2:
        dataList[item] = mushroom[item]
    default:
        dataList[item] = .background
    }
}

あとはボタン押下時に更新処理を実行するだけです!

@IBAction func change(_ sender: Any) {
    mode += 1
    if mode > 2 {
        mode = 0
    }

    var snapshot = dataSource.snapshot()
    let allItems = snapshot.itemIdentifiers
    let items = allItems.shuffled()
    snapshot.deleteItems(allItems)
    snapshot.appendItems(items, toSection: .main)
    snapshot.reloadItems(items)

    items.enumerated().forEach { item in
        switch mode {
        case 1:
            dataList[item.element] = bambooshoot[item.offset]
        case 2:
            dataList[item.element] = mushroom[item.offset]
        default:
            dataList[item.element] = .background
        }
    }

    dataSource.apply(snapshot, animatingDifferences: true)
}

おまけ

SpriteKit の SKEmitterNode(爆炎部分)と組み合わせてこんなこともできます:boom:

(再生するたけのこ)

ex

爆散と再生処理はこんな感じです。

/// 爆散処理
func shuffleDot() {
    var snapshot = dataSource.snapshot()
    let items = snapshot.itemIdentifiers
    snapshot.deleteItems(items)
    snapshot.appendItems(items.shuffled(), toSection: .main)
    dataSource.apply(snapshot, animatingDifferences: true)
}

/// 再生処理
func resetDot() {
    var snapshot = dataSource.snapshot()
    let items = snapshot.itemIdentifiers
    snapshot.deleteItems(items)
    snapshot.appendItems(items.sorted(by: { $1 > $0 }), toSection: .main)
    dataSource.apply(snapshot, animatingDifferences: true)
}

おわりに

みんなも UICollectionView を使ってドット絵をシュバババと表示しよう:sunglasses:

2
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
2
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?