11import UIKit
22
3+ /// ``PhotoScrubberCoupling`` にメイン画像/サムネイル両方のコンテンツを供給するデータソース。
4+ ///
5+ /// メイン (``PhotoScrubberCoupling/scrubView``) とストリップ
6+ /// (``PhotoScrubberCoupling/stripView``) は同じ item 数を共有する。両ビューの
7+ /// `index` は常に同じ item を指す。
38@MainActor
49public protocol PhotoScrubberDataSource : AnyObject {
10+ /// スクラバーが表示する item 数。メイン・サムネイルで共通。
511 func numberOfItems( in coupling: PhotoScrubberCoupling ) -> Int
12+ /// メイン (paging) 側の `index` 番目に表示する view を返す。
613 func photoScrubber( _ coupling: PhotoScrubberCoupling , mainViewAt index: Int ) -> UIView
14+ /// サムネイル帯の `index` 番目に表示する view を返す。
715 func photoScrubber( _ coupling: PhotoScrubberCoupling , thumbnailViewAt index: Int ) -> UIView
816}
917
18+ /// スクラブ操作の進行・表示 item 変化を受け取るデリゲート。全メソッドが任意実装。
1019@MainActor
1120public protocol PhotoScrubberDelegate : AnyObject {
21+ /// メイン/ストリップいずれかのスクロールで進行度が変化したとき呼ばれる。
22+ ///
23+ /// `progress` は `0...(itemCount - 1)` の連続値(item index の小数表現)。
1224 func photoScrubber( _ coupling: PhotoScrubberCoupling , didUpdateProgress progress: CGFloat )
25+ /// 表示中の item が別の index に切り替わったとき呼ばれる。
1326 func photoScrubber( _ coupling: PhotoScrubberCoupling , didChangeVisibleItem index: Int )
1427}
1528
@@ -18,25 +31,46 @@ public extension PhotoScrubberDelegate {
1831 func photoScrubber( _ coupling: PhotoScrubberCoupling , didChangeVisibleItem index: Int ) { }
1932}
2033
34+ /// Apple Photos.app 風スクラバーの中核。メイン画像ビューとサムネイル帯を双方向に連動させる。
35+ ///
36+ /// 一方をスクラブするともう一方が追従する。レイアウト(2 ビューの配置)は呼び出し側の責務で、
37+ /// 本クラスは ``scrubView`` / ``stripView`` の 2 つの `UIView` と結合ロジックだけを提供する。
38+ ///
39+ /// ## 使い方
40+ /// 1. ``dataSource`` を設定し、必要なら ``delegate`` / ``prefetcher`` も設定する。
41+ /// 2. ``scrubView`` と ``stripView`` を任意のレイアウトに配置する。
42+ /// 3. ``reloadData()`` を呼ぶ。
43+ ///
44+ /// データ変更は ``appendItem()`` / ``deleteItem(at:animated:)`` で両ビューへ一括反映される。
2145@MainActor
2246public final class PhotoScrubberCoupling {
2347
48+ /// メイン (paging) のスクラブビュー。
2449 public let scrubView : CustomScrubView
50+ /// サムネイル帯のストリップビュー。
2551 public let stripView : ScrubberStripView
2652
53+ /// item 数とメイン/サムネイル view を供給するデータソース。
2754 public weak var dataSource : ( any PhotoScrubberDataSource ) ?
55+ /// 進行度・表示 item 変化の通知先(任意)。
2856 public weak var delegate : ( any PhotoScrubberDelegate ) ?
57+ /// 周辺 item の事前ロード依頼先(任意)。
2958 public weak var prefetcher : ( any PhotoScrubberPrefetching ) ?
3059
31- /// `didChangeVisibleItem` 発火時に現在 index ± `mainPrefetchRadius` を main 用 prefetch として通知。
60+ /// `didChangeVisibleItem` 発火時に現在 index ± `mainPrefetchRadius` を main 用 prefetch として通知。既定 `2`。
3261 public var mainPrefetchRadius : Int = 2
3362
34- /// 同上、thumbnail 用。
63+ /// 同上、thumbnail 用。既定 `5`。
3564 public var thumbnailPrefetchRadius : Int = 5
3665
3766 private let forwardingProxy = ForwardingProxy ( )
3867 private var isProgrammaticUpdate = false
3968
69+ /// スクラバーを生成する。
70+ ///
71+ /// - Parameters:
72+ /// - scrubView: 既存のメインビューを使う場合に指定。`nil` なら内部で生成する。
73+ /// - stripView: 既存のストリップビューを使う場合に指定。`nil` なら内部で生成する。
4074 public init ( scrubView: CustomScrubView ? = nil ,
4175 stripView: ScrubberStripView ? = nil ) {
4276 self . scrubView = scrubView ?? CustomScrubView ( )
@@ -48,11 +82,18 @@ public final class PhotoScrubberCoupling {
4882 self . stripView. stripDelegate = forwardingProxy
4983 }
5084
85+ /// データソースを読み直し、メイン・サムネイル両方を再構築する。
5186 public func reloadData( ) {
5287 scrubView. reloadData ( )
5388 stripView. reloadData ( )
5489 }
5590
91+ /// 指定 index の item をメイン・サムネイル両方から削除する。
92+ ///
93+ /// 両ビューの削除を並行実行し、完了まで待つ。`async` なので呼び出し側で `await` する。
94+ /// - Parameters:
95+ /// - index: 削除する item の index。
96+ /// - animated: アニメーション付きで削除するか。
5697 public func deleteItem( at index: Int , animated: Bool ) async {
5798 isProgrammaticUpdate = true
5899 async let mainDone : Void = scrubView. deletePage ( at: index, animated: animated)
@@ -61,6 +102,10 @@ public final class PhotoScrubberCoupling {
61102 isProgrammaticUpdate = false
62103 }
63104
105+ /// 末尾に item を 1 つ追加し、メイン・サムネイル両方へ反映する。
106+ ///
107+ /// 追加後の item は ``dataSource`` から取得されるため、本メソッド呼び出し前に
108+ /// データソース側の件数を増やしておくこと。
64109 public func appendItem( ) {
65110 scrubView. appendPage ( )
66111 stripView. appendThumbnail ( )
0 commit comments