モノトーンの伝説日記

Apex Legends, Splatoon, Programming, and so on...

【UIKit.UIImageView】画像の注目して欲しいところを中心に表示する方法

 こんばんは,モノトーンです。

 今日は画像の注目して欲しいところ,いわゆる [Focus Point] を実装した UIImageView を作ろうというお話です。割と簡単なことなのですが,Focus Point 自体,実装されているサービスは Mastodon ぐらいしか知りませんw

 実際問題として,画像を中心に展開するコンテンツというのはそのまま全体を表示していることが多いですしね。ただ,絵の一部をピックアップして,例えばサムネでは顔を中心に,そしてタップすると全身になる,みたいなのは割と自然なシナリオだと思ってて,あってもいいのかなーと。

 まあ,次から具体的に前提知識から解説していきたいと思います。

1. UIKit の UIImageView の仕組み

 そもそも UIViewCALayer という Core Animation の Layer を 1:1 で持っていることは UIKit を触ったことある方なら誰でもご存知だと思います。UIView で 1:1 になっている CALayer は frame なども同期されますね。

 実は UIImageViewCALayerUIImage のもつ CGImage をセットしているだけの簡単な構造だと思われます*1

 CALayerGPU ベースで動くと考えられ,いわゆる Metal ベースな実装をしていると考えられます。

 それはさておき,画像をアスペクト比固定で表示するときに,contentMode を使うことは iOS アプリを作る上で誰しもが通る道だと思います。

developer.apple.com

 これ,実際の中身は CALayer の機能を使っていることをご存知でしたか?

 contentsGravity ですね。ほぼ 1:1 に対応していることがドキュメント見ていただければわかるかと。

developer.apple.com

 ということで,あくまで CALayercontentsCGImage がセットされているだけ,ということは理解できたと思います。

2. CALayer のプロパティー contentsRect

developer.apple.com

 x, y それぞれ [0, 1] の範囲で表示領域を決めるものです。

 例えば,画像の右下 1/4 を表示したい場合,(x, y) = (.5, .5), width = .5, height = .5 のように設定します。

 感のいい人ならわかると思いますが,focus point から表示したい領域を計算し,それをセットするだけで目的の機能が実装できるんですよね。お手軽です。

3. 実際の実装(簡易)

 Mastodon の focus point は以下のドキュメントを直接参照していますw

github.com

 まず focus point F(xf, yf) を [-1, 1] 空間から [0, 1] 空間に写像します。

 あとは画像を表示する領域 (Wc, Hc)と画像のサイズ (W, H) から,画像の表示する幅 Wまたは高さ H(以後長さ L と呼びます)を [0, 1] 空間で表現します。

 そして,始点 P を 0 以上になるように決めます。このとき画像の端になりすぎないように中心側に少し引っ張られるように調整します。

 始点 P に長さ L を足したものが 1 以下になるように調整します。

 こんな感じですね。詳しい計算式はコードを参照してください。そんなに難しいことはやってないです。

gist.github.com

 このコード,割り算の部分だけ SIMDKit に依存しています。一度に x, y 両方とも割り算するようになってるだけです。もし,SIMDKit 使いたくなければ分解するか,必要なコードだけ SIMDKit から移設してください。

SIMDKit/CGSize+Operators.swift at master · mntone/SIMDKit · GitHub

まとめ

 UIKit とか AppKit をここ三ヶ月は重点的に触っているのですが,やはり,CALayer 部分がよくできているなーって印象です。

 macOS でも動く MTImage っていうのを作っていたんですが,自前で draw すると window リサイズでカクカクになるのに対して,CALayer にセットした画像はスムーズにリサイズされて気持ちが良い現象が起こったので,CALayer ベースの実装にしていたりします。

github.com

 まあ,とりあえず iPhone が気持ちいいのは CALayer が素晴らしいっていうことで。

*1:UIImageView 自体 9-patch などに対応はしているので,そのあたりのコードは複雑そうですが