docs(recommendation): 서브 배너 리뷰와 QA 증거를 보존한다

This commit is contained in:
Yu Sung
2026-10-30 12:19:58 +09:00
parent 7a42db66bc
commit 32ab7797e0
206 changed files with 21989 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,466 @@
[
{
"time": 913597.4492290791,
"file": "0000.png"
},
{
"file": "0001.png",
"time": 913597.4755964761
},
{
"file": "0002.png",
"time": 913597.50850726
},
{
"time": 913597.5433671961,
"file": "0003.png"
},
{
"time": 913597.5739051071,
"file": "0004.png"
},
{
"file": "0005.png",
"time": 913597.609845889
},
{
"time": 913597.641820132,
"file": "0006.png"
},
{
"file": "0007.png",
"time": 913597.676881199
},
{
"file": "0008.png",
"time": 913597.7084638361
},
{
"file": "0009.png",
"time": 913597.7411874051
},
{
"file": "0010.png",
"time": 913597.775570434
},
{
"time": 913597.8080575881,
"file": "0011.png"
},
{
"file": "0012.png",
"time": 913597.84204845
},
{
"file": "0013.png",
"time": 913597.8778581431
},
{
"file": "0014.png",
"time": 913597.9616468931
},
{
"time": 913598.004829079,
"file": "0015.png"
},
{
"file": "0016.png",
"time": 913598.0422670391
},
{
"file": "0017.png",
"time": 913598.0752597761
},
{
"file": "0018.png",
"time": 913598.1080234471
},
{
"time": 913598.141075,
"file": "0019.png"
},
{
"file": "0020.png",
"time": 913598.175370774
},
{
"file": "0021.png",
"time": 913598.2073697711
},
{
"time": 913598.2420130861,
"file": "0022.png"
},
{
"file": "0023.png",
"time": 913598.2760424891
},
{
"time": 913598.306953603,
"file": "0024.png"
},
{
"time": 913598.3411505871,
"file": "0025.png"
},
{
"time": 913598.374023861,
"file": "0026.png"
},
{
"time": 913598.407951768,
"file": "0027.png"
},
{
"file": "0028.png",
"time": 913598.4401897051
},
{
"file": "0029.png",
"time": 913598.4735934021
},
{
"time": 913598.5078664881,
"file": "0030.png"
},
{
"file": "0031.png",
"time": 913598.5434376
},
{
"file": "0032.png",
"time": 913598.5772900771
},
{
"file": "0033.png",
"time": 913598.607618751
},
{
"file": "0034.png",
"time": 913598.641246484
},
{
"time": 913598.6736933991,
"file": "0035.png"
},
{
"file": "0036.png",
"time": 913598.7074893891
},
{
"file": "0037.png",
"time": 913598.743114041
},
{
"file": "0038.png",
"time": 913598.7739387951
},
{
"file": "0039.png",
"time": 913598.808327698
},
{
"time": 913598.841209955,
"file": "0040.png"
},
{
"file": "0041.png",
"time": 913598.875832265
},
{
"file": "0042.png",
"time": 913598.908611273
},
{
"file": "0043.png",
"time": 913598.9920293391
},
{
"time": 913599.0429295121,
"file": "0044.png"
},
{
"file": "0045.png",
"time": 913599.073915453
},
{
"time": 913599.1069630211,
"file": "0046.png"
},
{
"file": "0047.png",
"time": 913599.139273638
},
{
"file": "0048.png",
"time": 913599.171037629
},
{
"file": "0049.png",
"time": 913599.2040882771
},
{
"time": 913599.2386047101,
"file": "0050.png"
},
{
"file": "0051.png",
"time": 913599.2723036291
},
{
"time": 913599.3079158181,
"file": "0052.png"
},
{
"file": "0053.png",
"time": 913599.3431351511
},
{
"time": 913599.376199943,
"file": "0054.png"
},
{
"file": "0055.png",
"time": 913599.4078560281
},
{
"time": 913599.440783828,
"file": "0056.png"
},
{
"time": 913599.4710975741,
"file": "0057.png"
},
{
"file": "0058.png",
"time": 913599.5026518331
},
{
"time": 913599.5386346581,
"file": "0059.png"
},
{
"time": 913599.5707385921,
"file": "0060.png"
},
{
"file": "0061.png",
"time": 913599.602594416
},
{
"time": 913599.6393803691,
"file": "0062.png"
},
{
"file": "0063.png",
"time": 913599.6682303511
},
{
"file": "0064.png",
"time": 913599.7060550201
},
{
"time": 913599.736194105,
"file": "0065.png"
},
{
"file": "0066.png",
"time": 913599.7714051381
},
{
"time": 913599.802721531,
"file": "0067.png"
},
{
"time": 913599.873930242,
"file": "0068.png"
},
{
"time": 913599.893517905,
"file": "0069.png"
},
{
"file": "0070.png",
"time": 913599.913150748
},
{
"file": "0071.png",
"time": 913599.942140343
},
{
"time": 913599.97637692,
"file": "0072.png"
},
{
"time": 913600.0079948221,
"file": "0073.png"
},
{
"file": "0074.png",
"time": 913600.0425524261
},
{
"file": "0075.png",
"time": 913600.0739939511
},
{
"time": 913600.109444809,
"file": "0076.png"
},
{
"time": 913600.14165221,
"file": "0077.png"
},
{
"time": 913600.1751490401,
"file": "0078.png"
},
{
"time": 913600.207547056,
"file": "0079.png"
},
{
"time": 913600.241096996,
"file": "0080.png"
},
{
"file": "0081.png",
"time": 913600.2762579471
},
{
"file": "0082.png",
"time": 913600.306952715
},
{
"time": 913600.3412508611,
"file": "0083.png"
},
{
"time": 913600.3752544611,
"file": "0084.png"
},
{
"file": "0085.png",
"time": 913600.4240071401
},
{
"time": 913600.4467707721,
"file": "0086.png"
},
{
"time": 913600.4733675481,
"file": "0087.png"
},
{
"time": 913600.507895805,
"file": "0088.png"
},
{
"file": "0089.png",
"time": 913600.5426770151
},
{
"file": "0090.png",
"time": 913600.573082122
},
{
"file": "0091.png",
"time": 913600.6076171291
},
{
"file": "0092.png",
"time": 913600.6396793531
},
{
"file": "0093.png",
"time": 913600.675508002
},
{
"file": "0094.png",
"time": 913600.7075339471
},
{
"time": 913600.74025594,
"file": "0095.png"
},
{
"time": 913600.775323804,
"file": "0096.png"
},
{
"file": "0097.png",
"time": 913600.8072155941
},
{
"time": 913600.8411561421,
"file": "0098.png"
},
{
"file": "0099.png",
"time": 913600.8750394251
},
{
"file": "0100.png",
"time": 913600.90762452
},
{
"file": "0101.png",
"time": 913600.940233796
},
{
"time": 913600.9744563231,
"file": "0102.png"
},
{
"time": 913601.00685121,
"file": "0103.png"
},
{
"file": "0104.png",
"time": 913601.040866059
},
{
"time": 913601.074510779,
"file": "0105.png"
},
{
"file": "0106.png",
"time": 913601.1079004781
},
{
"file": "0107.png",
"time": 913601.1438779951
},
{
"file": "0108.png",
"time": 913601.1751519741
},
{
"time": 913601.2079005501,
"file": "0109.png"
},
{
"file": "0110.png",
"time": 913601.24195652
},
{
"time": 913601.277139337,
"file": "0111.png"
},
{
"time": 913601.307590552,
"file": "0112.png"
},
{
"file": "0113.png",
"time": 913601.3412412221
},
{
"time": 913601.3733143321,
"file": "0114.png"
},
{
"file": "0115.png",
"time": 913601.4083740071
}
]
@@ -0,0 +1,362 @@
[
{
"pid": 34695,
"test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]",
"time": 913588.4641189601,
"event": "launch"
},
{
"event": "phase-inactive",
"pid": 34695,
"time": 913589.395679436,
"test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]"
},
{
"event": "phase-active",
"time": 913589.412622352,
"pid": 34695,
"test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]"
},
{
"pid": 34695,
"event": "record-automatic",
"time": 913597.4139605111,
"test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]"
},
{
"test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]",
"pid": 34695,
"time": 913602.1043090811,
"event": "tap-1"
},
{
"time": 913604.48309818,
"pid": 34735,
"event": "launch",
"test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]"
},
{
"pid": 34735,
"event": "phase-inactive",
"time": 913605.391383399,
"test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]"
},
{
"test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]",
"event": "phase-active",
"time": 913605.4323043161,
"pid": 34735
},
{
"event": "tap-1",
"time": 913608.887246688,
"pid": 34735,
"test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]"
},
{
"event": "tap-3",
"time": 913610.570216782,
"pid": 34735,
"test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]"
},
{
"event": "tap-1",
"time": 913613.587584087,
"pid": 34735,
"test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]"
},
{
"pid": 34767,
"test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]",
"event": "launch",
"time": 913615.994330842
},
{
"test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]",
"event": "phase-inactive",
"time": 913616.8966431321,
"pid": 34767
},
{
"pid": 34767,
"event": "phase-active",
"time": 913616.9213246681,
"test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]"
},
{
"test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]",
"time": 913625.4543779121,
"event": "record-hide",
"pid": 34767
},
{
"pid": 34767,
"test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]",
"time": 913627.272562311,
"event": "observed-mixed-hide"
},
{
"time": 913627.273564405,
"pid": 34767,
"test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]",
"event": "hide"
},
{
"event": "show",
"time": 913628.662756823,
"pid": 34767,
"test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]"
},
{
"test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]",
"time": 913632.1317507351,
"pid": 34805,
"event": "launch"
},
{
"time": 913633.0326134231,
"pid": 34805,
"test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]",
"event": "phase-inactive"
},
{
"time": 913633.0621602761,
"event": "phase-active",
"pid": 34805,
"test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]"
},
{
"test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]",
"time": 913636.61430926,
"pid": 34805,
"event": "phase-inactive"
},
{
"time": 913637.3474018341,
"pid": 34805,
"test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]",
"event": "phase-background"
},
{
"event": "phase-inactive",
"time": 913642.895329179,
"pid": 34805,
"test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]"
},
{
"pid": 34805,
"test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]",
"event": "phase-active",
"time": 913643.185701646
},
{
"pid": 34837,
"test": "-[SubBannerGestureUITests testManualBoundaryFrames]",
"event": "launch",
"time": 913645.102787193
},
{
"event": "phase-inactive",
"pid": 34837,
"time": 913645.9961799241,
"test": "-[SubBannerGestureUITests testManualBoundaryFrames]"
},
{
"test": "-[SubBannerGestureUITests testManualBoundaryFrames]",
"time": 913646.027965715,
"event": "phase-active",
"pid": 34837
},
{
"time": 913649.4333553461,
"test": "-[SubBannerGestureUITests testManualBoundaryFrames]",
"pid": 34837,
"event": "record-manual"
},
{
"pid": 34837,
"test": "-[SubBannerGestureUITests testManualBoundaryFrames]",
"event": "record-forward",
"time": 913652.2542932191
},
{
"test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]",
"pid": 34870,
"time": 913657.008808529,
"event": "launch"
},
{
"time": 913657.9150553821,
"event": "phase-inactive",
"pid": 34870,
"test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]"
},
{
"test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]",
"event": "phase-active",
"time": 913657.9402682821,
"pid": 34870
},
{
"time": 913664.836766332,
"test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]",
"pid": 34870,
"event": "replace"
},
{
"event": "tap-9",
"time": 913665.2533201941,
"pid": 34870,
"test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]"
},
{
"time": 913667.64497189,
"test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]",
"pid": 34894,
"event": "launch"
},
{
"event": "phase-inactive",
"time": 913668.5523268571,
"pid": 34894,
"test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]"
},
{
"test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]",
"event": "phase-active",
"pid": 34894,
"time": 913668.589080931
},
{
"time": 913677.1188918981,
"test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]",
"pid": 34894,
"event": "record-replace"
},
{
"event": "observed-mixed-replace",
"test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]",
"time": 913678.958359765,
"pid": 34894
},
{
"test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]",
"time": 913678.959777174,
"pid": 34894,
"event": "replace"
},
{
"time": 913680.7371332,
"event": "tap-9",
"test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]",
"pid": 34894
},
{
"pid": 34933,
"test": "-[SubBannerGestureUITests testSingleAndEmpty]",
"event": "launch",
"time": 913683.2259921201
},
{
"event": "phase-inactive",
"pid": 34933,
"time": 913684.1276096171,
"test": "-[SubBannerGestureUITests testSingleAndEmpty]"
},
{
"pid": 34933,
"test": "-[SubBannerGestureUITests testSingleAndEmpty]",
"event": "phase-active",
"time": 913684.1499581131
},
{
"time": 913687.586987999,
"pid": 34933,
"event": "tap-1",
"test": "-[SubBannerGestureUITests testSingleAndEmpty]"
},
{
"test": "-[SubBannerGestureUITests testSingleAndEmpty]",
"time": 913690.6921521671,
"event": "launch",
"pid": 34951
},
{
"time": 913691.5782256341,
"test": "-[SubBannerGestureUITests testSingleAndEmpty]",
"event": "phase-inactive",
"pid": 34951
},
{
"time": 913691.5995834051,
"event": "phase-active",
"pid": 34951,
"test": "-[SubBannerGestureUITests testSingleAndEmpty]"
},
{
"time": 913696.0071675461,
"pid": 34968,
"test": "-[SubBannerGestureUITests testSwipeDoesNotTap]",
"event": "launch"
},
{
"pid": 34968,
"time": 913696.9234373821,
"event": "phase-inactive",
"test": "-[SubBannerGestureUITests testSwipeDoesNotTap]"
},
{
"event": "phase-active",
"pid": 34968,
"test": "-[SubBannerGestureUITests testSwipeDoesNotTap]",
"time": 913696.949425647
},
{
"event": "tap-2",
"test": "-[SubBannerGestureUITests testSwipeDoesNotTap]",
"time": 913701.720210077,
"pid": 34968
},
{
"event": "launch",
"pid": 34991,
"test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]",
"time": 913704.0864146911
},
{
"event": "phase-inactive",
"test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]",
"time": 913704.9734107051,
"pid": 34991
},
{
"pid": 34991,
"event": "phase-active",
"time": 913705.0040099691,
"test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]"
},
{
"test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]",
"event": "tap-1",
"time": 913712.636718675,
"pid": 34991
},
{
"pid": 35015,
"event": "launch",
"time": 913715.053948565,
"test": "-[SubBannerGestureUITests testVerticalParentScrollDoesNotTap]"
},
{
"time": 913715.943832408,
"pid": 35015,
"test": "-[SubBannerGestureUITests testVerticalParentScrollDoesNotTap]",
"event": "phase-inactive"
},
{
"pid": 35015,
"event": "phase-active",
"test": "-[SubBannerGestureUITests testVerticalParentScrollDoesNotTap]",
"time": 913715.967804307
}
]
@@ -0,0 +1 @@
b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift
@@ -0,0 +1 @@
b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift
@@ -0,0 +1 @@
b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift
@@ -0,0 +1,5 @@
PASS: actual APIs/repositories, two exact GETs, requestPlain, anonymous/whitespace auth, original bearer, adjacent follow POST/body/header
PASS: actual two VMs reject non200 valid/invalid JSON, success false, decode/transport errors; preserve error UI and loading
PASS: actual DTOs/VM success, >2^53 and Int.max, enum, required array, 23+ full order, placement/eligibility, legacy banner, subbanner-only/filtered-empty, failure retains data
PASS: HTTP(S)+host, absolute URI/blacklist, malformed/blank/auto-encoded rejection, exact escaped URI, all 8 link combinations, completion-based iOS/web attempts, Android never
PASS: sub-banner production-code checks; native Combine, Moya transport double; no live requests
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,282 @@
[
{
"time": 913652.291601978,
"file": "0000.png"
},
{
"time": 913652.311797108,
"file": "0001.png"
},
{
"time": 913652.3391558371,
"file": "0002.png"
},
{
"time": 913652.376711928,
"file": "0003.png"
},
{
"file": "0004.png",
"time": 913652.4078878481
},
{
"file": "0005.png",
"time": 913652.4395461461
},
{
"file": "0006.png",
"time": 913652.4726654671
},
{
"time": 913652.538570735,
"file": "0007.png"
},
{
"time": 913652.586738405,
"file": "0008.png"
},
{
"file": "0009.png",
"time": 913652.60548328
},
{
"file": "0010.png",
"time": 913652.663007152
},
{
"file": "0011.png",
"time": 913652.682149627
},
{
"file": "0012.png",
"time": 913652.7373864851
},
{
"file": "0013.png",
"time": 913652.805418989
},
{
"time": 913652.904987868,
"file": "0014.png"
},
{
"time": 913652.9665794891,
"file": "0015.png"
},
{
"file": "0016.png",
"time": 913652.987309437
},
{
"time": 913653.0076745551,
"file": "0017.png"
},
{
"file": "0018.png",
"time": 913653.039906566
},
{
"time": 913653.0749919331,
"file": "0019.png"
},
{
"time": 913653.109595312,
"file": "0020.png"
},
{
"file": "0021.png",
"time": 913653.138689461
},
{
"file": "0022.png",
"time": 913653.1710794021
},
{
"file": "0023.png",
"time": 913653.2016720751
},
{
"file": "0024.png",
"time": 913653.2382149621
},
{
"time": 913653.268156755,
"file": "0025.png"
},
{
"file": "0026.png",
"time": 913653.3231254701
},
{
"file": "0027.png",
"time": 913653.3458972421
},
{
"time": 913653.3699691831,
"file": "0028.png"
},
{
"time": 913653.4018004481,
"file": "0029.png"
},
{
"file": "0030.png",
"time": 913653.4518237461
},
{
"time": 913653.472333822,
"file": "0031.png"
},
{
"file": "0032.png",
"time": 913653.520925558
},
{
"file": "0033.png",
"time": 913653.5443326001
},
{
"file": "0034.png",
"time": 913653.5703751601
},
{
"file": "0035.png",
"time": 913653.6053882821
},
{
"file": "0036.png",
"time": 913653.6348647911
},
{
"file": "0037.png",
"time": 913653.6737473761
},
{
"file": "0038.png",
"time": 913653.701857235
},
{
"file": "0039.png",
"time": 913653.7411189
},
{
"file": "0040.png",
"time": 913653.798327717
},
{
"file": "0041.png",
"time": 913653.8416348731
},
{
"time": 913653.8685095541,
"file": "0042.png"
},
{
"file": "0043.png",
"time": 913653.904678173
},
{
"time": 913653.9346417851,
"file": "0044.png"
},
{
"time": 913653.9724434691,
"file": "0045.png"
},
{
"file": "0046.png",
"time": 913654.0039547511
},
{
"file": "0047.png",
"time": 913654.03900551
},
{
"time": 913654.072691896,
"file": "0048.png"
},
{
"file": "0049.png",
"time": 913654.1242488661
},
{
"file": "0050.png",
"time": 913654.145452633
},
{
"time": 913654.172808056,
"file": "0051.png"
},
{
"file": "0052.png",
"time": 913654.2075264001
},
{
"time": 913654.242221885,
"file": "0053.png"
},
{
"time": 913654.276169833,
"file": "0054.png"
},
{
"time": 913654.306327088,
"file": "0055.png"
},
{
"file": "0056.png",
"time": 913654.341744952
},
{
"file": "0057.png",
"time": 913654.3735942411
},
{
"file": "0058.png",
"time": 913654.407528787
},
{
"file": "0059.png",
"time": 913654.4408740861
},
{
"file": "0060.png",
"time": 913654.475743551
},
{
"time": 913654.5077910591,
"file": "0061.png"
},
{
"file": "0062.png",
"time": 913654.5393077991
},
{
"time": 913654.5721815861,
"file": "0063.png"
},
{
"file": "0064.png",
"time": 913654.605473313
},
{
"file": "0065.png",
"time": 913654.6419051881
},
{
"file": "0066.png",
"time": 913654.6744255021
},
{
"file": "0067.png",
"time": 913654.705513195
},
{
"file": "0068.png",
"time": 913654.740565455
},
{
"file": "0069.png",
"time": 913654.7747490801
}
]
@@ -0,0 +1,4 @@
fb9c6a4fa0ada92335df184de26eba1f560d243ae7ee0a22df92a3ab0841bba0 work/tests/SubBannerGestureHarness.swift
17ca9e2b84ede28fcd69043585a0ae8fc684e889c86196431ddb67904ae49f94 work/tests/SubBannerGestureUITests.swift
d1ace05fd9a0e703aec1eeada0630951b69aff0094e51d2e2824c6f059c51cb0 work/tests/run-sub-banner-gesture-tests.sh
90d49c05bd5b2ef8872842fd0dcbb372687ad363fd8293417d61e320c33ada8c work/tests/generate-sub-banner-gesture-project.rb
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,478 @@
[
{
"file": "0000.png",
"time": 913625.5146856791
},
{
"file": "0001.png",
"time": 913625.543152694
},
{
"time": 913625.578383538,
"file": "0002.png"
},
{
"file": "0003.png",
"time": 913625.6081350951
},
{
"file": "0004.png",
"time": 913625.642946815
},
{
"file": "0005.png",
"time": 913625.675942062
},
{
"file": "0006.png",
"time": 913625.7072436911
},
{
"time": 913625.7405944001,
"file": "0007.png"
},
{
"file": "0008.png",
"time": 913625.772961818
},
{
"file": "0009.png",
"time": 913625.8083248751
},
{
"file": "0010.png",
"time": 913625.839961951
},
{
"file": "0011.png",
"time": 913625.875382437
},
{
"file": "0012.png",
"time": 913625.907734352
},
{
"time": 913625.9399363911,
"file": "0013.png"
},
{
"file": "0014.png",
"time": 913625.9756715071
},
{
"time": 913626.0076115291,
"file": "0015.png"
},
{
"time": 913626.040202938,
"file": "0016.png"
},
{
"time": 913626.0763464931,
"file": "0017.png"
},
{
"time": 913626.1074425881,
"file": "0018.png"
},
{
"file": "0019.png",
"time": 913626.142858917
},
{
"time": 913626.174399673,
"file": "0020.png"
},
{
"file": "0021.png",
"time": 913626.2089162191
},
{
"time": 913626.245077291,
"file": "0022.png"
},
{
"file": "0023.png",
"time": 913626.273724116
},
{
"time": 913626.308378434,
"file": "0024.png"
},
{
"time": 913626.342996428,
"file": "0025.png"
},
{
"file": "0026.png",
"time": 913626.375832002
},
{
"file": "0027.png",
"time": 913626.4081797751
},
{
"file": "0028.png",
"time": 913626.4584254951
},
{
"time": 913626.47900169,
"file": "0029.png"
},
{
"file": "0030.png",
"time": 913626.505358272
},
{
"file": "0031.png",
"time": 913626.539463017
},
{
"file": "0032.png",
"time": 913626.573096725
},
{
"file": "0033.png",
"time": 913626.608633795
},
{
"time": 913626.6417559021,
"file": "0034.png"
},
{
"file": "0035.png",
"time": 913626.6760256
},
{
"time": 913626.707807358,
"file": "0036.png"
},
{
"file": "0037.png",
"time": 913626.7404586211
},
{
"file": "0038.png",
"time": 913626.7742304481
},
{
"time": 913626.8069071311,
"file": "0039.png"
},
{
"time": 913626.841651942,
"file": "0040.png"
},
{
"time": 913626.875099051,
"file": "0041.png"
},
{
"file": "0042.png",
"time": 913626.906885443
},
{
"file": "0043.png",
"time": 913626.9402810431
},
{
"file": "0044.png",
"time": 913626.9741933261
},
{
"file": "0045.png",
"time": 913627.0088141051
},
{
"file": "0046.png",
"time": 913627.04069364
},
{
"file": "0047.png",
"time": 913627.0738882851
},
{
"time": 913627.106783885,
"file": "0048.png"
},
{
"file": "0049.png",
"time": 913627.142314809
},
{
"file": "0050.png",
"time": 913627.1741426891
},
{
"file": "0051.png",
"time": 913627.207692999
},
{
"time": 913627.2382389171,
"file": "0052.png"
},
{
"time": 913627.2715684241,
"file": "0053.png"
},
{
"file": "0054.png",
"time": 913627.3016557251
},
{
"file": "0055.png",
"time": 913627.3565241981
},
{
"time": 913627.3890422831,
"file": "0056.png"
},
{
"time": 913627.410564169,
"file": "0057.png"
},
{
"file": "0058.png",
"time": 913627.44241933
},
{
"time": 913627.47227425,
"file": "0059.png"
},
{
"time": 913627.503578981,
"file": "0060.png"
},
{
"time": 913627.53656089,
"file": "0061.png"
},
{
"time": 913627.5706346061,
"file": "0062.png"
},
{
"time": 913627.6059774631,
"file": "0063.png"
},
{
"file": "0064.png",
"time": 913627.642300406
},
{
"time": 913627.672786911,
"file": "0065.png"
},
{
"file": "0066.png",
"time": 913627.7059342221
},
{
"file": "0067.png",
"time": 913627.7404805991
},
{
"file": "0068.png",
"time": 913627.772288126
},
{
"file": "0069.png",
"time": 913627.806091586
},
{
"file": "0070.png",
"time": 913627.8423069571
},
{
"time": 913627.872227921,
"file": "0071.png"
},
{
"file": "0072.png",
"time": 913627.904815815
},
{
"time": 913627.9391412591,
"file": "0073.png"
},
{
"file": "0074.png",
"time": 913627.974508933
},
{
"time": 913628.0064958821,
"file": "0075.png"
},
{
"time": 913628.0413351611,
"file": "0076.png"
},
{
"file": "0077.png",
"time": 913628.072915519
},
{
"file": "0078.png",
"time": 913628.1060072781
},
{
"file": "0079.png",
"time": 913628.141235796
},
{
"file": "0080.png",
"time": 913628.1745066891
},
{
"file": "0081.png",
"time": 913628.20653584
},
{
"file": "0082.png",
"time": 913628.240295661
},
{
"time": 913628.274166323,
"file": "0083.png"
},
{
"time": 913628.306417725,
"file": "0084.png"
},
{
"file": "0085.png",
"time": 913628.341532737
},
{
"time": 913628.3915037521,
"file": "0086.png"
},
{
"file": "0087.png",
"time": 913628.411066954
},
{
"time": 913628.455057331,
"file": "0088.png"
},
{
"file": "0089.png",
"time": 913628.474613611
},
{
"time": 913628.503860494,
"file": "0090.png"
},
{
"file": "0091.png",
"time": 913628.5366643631
},
{
"file": "0092.png",
"time": 913628.573629267
},
{
"file": "0093.png",
"time": 913628.604441763
},
{
"time": 913628.6594196,
"file": "0094.png"
},
{
"file": "0095.png",
"time": 913628.6832831871
},
{
"file": "0096.png",
"time": 913628.7330405031
},
{
"time": 913628.77642887,
"file": "0097.png"
},
{
"file": "0098.png",
"time": 913628.8043427521
},
{
"file": "0099.png",
"time": 913628.840021964
},
{
"file": "0100.png",
"time": 913628.8716845771
},
{
"time": 913628.905594829,
"file": "0101.png"
},
{
"file": "0102.png",
"time": 913628.93960048
},
{
"file": "0103.png",
"time": 913628.9711876811
},
{
"time": 913629.0029976941,
"file": "0104.png"
},
{
"file": "0105.png",
"time": 913629.0400746231
},
{
"file": "0106.png",
"time": 913629.071553146
},
{
"file": "0107.png",
"time": 913629.103350113
},
{
"file": "0108.png",
"time": 913629.140292271
},
{
"time": 913629.168505785,
"file": "0109.png"
},
{
"time": 913629.207598084,
"file": "0110.png"
},
{
"file": "0111.png",
"time": 913629.236739716
},
{
"time": 913629.272875122,
"file": "0112.png"
},
{
"file": "0113.png",
"time": 913629.307721958
},
{
"file": "0114.png",
"time": 913629.3432565581
},
{
"file": "0115.png",
"time": 913629.3731304981
},
{
"file": "0116.png",
"time": 913629.407447105
},
{
"file": "0117.png",
"time": 913629.4423861081
},
{
"time": 913629.477664105,
"file": "0118.png"
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

@@ -0,0 +1,270 @@
[
{
"file": "0000.png",
"time": 913649.4789123321
},
{
"file": "0001.png",
"time": 913649.5409981351
},
{
"file": "0002.png",
"time": 913649.5719159461
},
{
"time": 913649.604001975,
"file": "0003.png"
},
{
"file": "0004.png",
"time": 913649.637579345
},
{
"file": "0005.png",
"time": 913649.6715946691
},
{
"time": 913649.7373082781,
"file": "0006.png"
},
{
"file": "0007.png",
"time": 913649.773161266
},
{
"file": "0008.png",
"time": 913649.837708718
},
{
"file": "0009.png",
"time": 913649.897244942
},
{
"file": "0010.png",
"time": 913649.916900538
},
{
"file": "0011.png",
"time": 913650.0185351061
},
{
"file": "0012.png",
"time": 913650.040369812
},
{
"file": "0013.png",
"time": 913650.0959979381
},
{
"file": "0014.png",
"time": 913650.115852643
},
{
"time": 913650.1387285291,
"file": "0015.png"
},
{
"time": 913650.1890770551,
"file": "0016.png"
},
{
"time": 913650.209703319,
"file": "0017.png"
},
{
"file": "0018.png",
"time": 913650.257690393
},
{
"file": "0019.png",
"time": 913650.2955483911
},
{
"file": "0020.png",
"time": 913650.321425457
},
{
"file": "0021.png",
"time": 913650.352865361
},
{
"time": 913650.3855440071,
"file": "0022.png"
},
{
"file": "0023.png",
"time": 913650.422998685
},
{
"time": 913650.4526552961,
"file": "0024.png"
},
{
"file": "0025.png",
"time": 913650.485515214
},
{
"time": 913650.5055894121,
"file": "0026.png"
},
{
"file": "0027.png",
"time": 913650.5543494751
},
{
"time": 913650.5751487471,
"file": "0028.png"
},
{
"file": "0029.png",
"time": 913650.620362143
},
{
"file": "0030.png",
"time": 913650.640958155
},
{
"file": "0031.png",
"time": 913650.6845618271
},
{
"time": 913650.7045911091,
"file": "0032.png"
},
{
"file": "0033.png",
"time": 913650.734892515
},
{
"time": 913650.774539203,
"file": "0034.png"
},
{
"file": "0035.png",
"time": 913650.8017526851
},
{
"file": "0036.png",
"time": 913650.8394169301
},
{
"file": "0037.png",
"time": 913650.8681549091
},
{
"file": "0038.png",
"time": 913650.926757905
},
{
"time": 913650.972167773,
"file": "0039.png"
},
{
"time": 913651.03541228,
"file": "0040.png"
},
{
"time": 913651.109038961,
"file": "0041.png"
},
{
"time": 913651.1392424961,
"file": "0042.png"
},
{
"time": 913651.175997302,
"file": "0043.png"
},
{
"file": "0044.png",
"time": 913651.207577649
},
{
"time": 913651.2412511851,
"file": "0045.png"
},
{
"file": "0046.png",
"time": 913651.272701774
},
{
"file": "0047.png",
"time": 913651.307512223
},
{
"file": "0048.png",
"time": 913651.343124047
},
{
"time": 913651.3754428921,
"file": "0049.png"
},
{
"time": 913651.4083072931,
"file": "0050.png"
},
{
"file": "0051.png",
"time": 913651.441592662
},
{
"file": "0052.png",
"time": 913651.4762968901
},
{
"file": "0053.png",
"time": 913651.506200829
},
{
"time": 913651.5413629961,
"file": "0054.png"
},
{
"file": "0055.png",
"time": 913651.5741396641
},
{
"time": 913651.607966713,
"file": "0056.png"
},
{
"file": "0057.png",
"time": 913651.640467989
},
{
"file": "0058.png",
"time": 913651.675139843
},
{
"time": 913651.707729437,
"file": "0059.png"
},
{
"time": 913651.740942755,
"file": "0060.png"
},
{
"time": 913651.7733378131,
"file": "0061.png"
},
{
"time": 913651.805534414,
"file": "0062.png"
},
{
"file": "0063.png",
"time": 913651.8411757961
},
{
"file": "0064.png",
"time": 913651.876669439
},
{
"time": 913651.9073031051,
"file": "0065.png"
},
{
"time": 913651.941832795,
"file": "0066.png"
}
]
@@ -0,0 +1,69 @@
Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' started.
t = 17.06s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' passed (17.665 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testDirectionsWrapShortDragAndTap]' started.
t = 6.17s Synthesize event
t = 6.54s Press "1 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 6.62s Synthesize event
t = 7.74s Synthesize event
t = 8.22s Press "3 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "3 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 8.30s Synthesize event
t = 9.34s Press "1 / 3" Button[0.50, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.41, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 9.42s Synthesize event
t = 10.10s Press "1 / 3" Button[0.50, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.46, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 10.18s Synthesize event
t = 10.88s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testDirectionsWrapShortDragAndTap]' passed (11.506 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]' started.
t = 14.44s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]' passed (16.138 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]' started.
Test Case '-[GestureUITests.SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]' passed (12.944 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' started.
t = 6.14s Synthesize event
t = 6.46s Press "1 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 6.59s Synthesize event
t = 8.96s Synthesize event
t = 9.29s Press "3 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "3 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 9.42s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' passed (11.906 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testMixedRatiosAndReplacement]' started.
t = 6.18s Press "1 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 6.26s Synthesize event
t = 7.32s Press "2 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "2 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 7.41s Synthesize event
t = 8.39s Press "1 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 8.47s Synthesize event
t = 9.63s Synthesize event
t = 10.06s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testMixedRatiosAndReplacement]' passed (10.653 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testReplacementDuringAutomaticWrap]' started.
t = 14.86s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testReplacementDuringAutomaticWrap]' passed (15.490 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testSingleAndEmpty]' started.
t = 6.12s Synthesize event
t = 6.47s Press "1 / 1" Button[0.80, 0.50] for 0.1 seconds, then drag to "1 / 1" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 6.55s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testSingleAndEmpty]' passed (12.696 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' started.
t = 6.14s Press "1 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 6.23s Synthesize event
t = 7.37s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' passed (8.157 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]' started.
t = 6.05s Press "1 / 3" Button[0.90, 0.50] for 0.2 seconds, then drag to "1 / 3" Button[0.60, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 6.14s Synthesize event
t = 8.18s Press "2 / 3" Button[0.70, 0.50] for 0.0 seconds, then drag to "2 / 3" Button[0.15, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 8.26s Synthesize event
t = 9.13s Press "3 / 3" Button[0.60, 0.50] for 0.1 seconds, then drag to "3 / 3" Button[0.10, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 9.21s Synthesize event
t = 10.32s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]' passed (10.950 seconds).
Test Case '-[GestureUITests.SubBannerGestureUITests testVerticalParentScrollDoesNotTap]' started.
t = 6.10s Press "1 / 3" Button[0.50, 0.90] for 0.1 seconds, then drag to "1 / 3" Button[0.50, 0.10] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds
t = 6.18s Synthesize event
Test Case '-[GestureUITests.SubBannerGestureUITests testVerticalParentScrollDoesNotTap]' passed (9.099 seconds).
Executed 11 tests, with 0 failures (0 unexpected) in 137.204 (137.216) seconds
Executed 11 tests, with 0 failures (0 unexpected) in 137.204 (137.217) seconds
Executed 11 tests, with 0 failures (0 unexpected) in 137.204 (137.219) seconds
** TEST SUCCEEDED **
@@ -0,0 +1,181 @@
import SwiftUI
import Kingfisher
struct SubBannerCarousel: View {
let items: [SubBanner]
let onTap: (SubBanner) -> Void
@Environment(\.scenePhase) private var scenePhase
@State private var displayIndex = 1
@State private var itemsRevision = 0
@State private var containerWidth: CGFloat = 0
@State private var imageRatios: [String: CGFloat] = [:]
@State private var lastKnownRatio: CGFloat?
@GestureState private var isSwiping = false
@GestureState private var dragOffset: CGFloat = 0
private static let autoScrollNanoseconds: UInt64 = 5_000_000_000
private static let transitionDuration: TimeInterval = 0.35
private var safeDisplayIndex: Int {
items.count > 1 ? min(max(displayIndex, 0), items.count + 1) : 0
}
private var safeIndex: Int {
items.count > 1 ? (safeDisplayIndex + items.count - 1) % items.count : 0
}
private var pages: [(id: String, item: SubBanner)] {
let originals = items.map { (id: "item-\($0.id)", item: $0) }
guard items.count > 1, let first = items.first, let last = items.last else { return originals }
return [(id: "leading-\(last.id)", item: last)] + originals + [(id: "trailing-\(first.id)", item: first)]
}
private var currentRatio: CGFloat? {
guard !items.isEmpty else { return nil }
return imageRatios[items[safeIndex].imageUrl]
}
private var height: CGFloat {
guard !items.isEmpty, let ratio = currentRatio ?? lastKnownRatio else { return 0 }
return containerWidth / ratio
}
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0) {
ForEach(pages, id: \.id) { page in
Button {
onTap(page.item)
} label: {
KFImage(page.item.imageURL)
.placeholder { Color.gray800 }
.onSuccess { result in
let size = result.image.size
guard size.width > 0, size.height > 0 else { return }
imageRatios[page.item.imageUrl] = size.width / size.height
}
.startLoadingBeforeViewAppear()
.resizable()
.scaledToFit()
.frame(width: geometry.size.width, height: height)
.background(Color.gray800)
}
.buttonStyle(SubBannerTapStyle(drag: dragGesture(width: geometry.size.width)))
.accessibilityLabel(Text("\(safeIndex + 1) / \(items.count)"))
.accessibilityHidden(pages[safeDisplayIndex].id != page.id)
}
}
.offset(x: -CGFloat(safeDisplayIndex) * geometry.size.width + dragOffset)
.animation(.easeOut(duration: Self.transitionDuration), value: displayIndex)
.frame(width: geometry.size.width, height: height, alignment: .leading)
.contentShape(Rectangle())
.clipped()
.overlay(alignment: .topTrailing) {
if !items.isEmpty && height > 0 {
pageIndicator
.padding(SodaSpacing.s14)
}
}
.onAppear { containerWidth = geometry.size.width }
.onChange(of: geometry.size.width) { containerWidth = $0 }
}
.frame(height: height)
.onChange(of: currentRatio) { ratio in
if let ratio { lastKnownRatio = ratio }
}
.onChange(of: items) { _ in
itemsRevision += 1
normalize(to: items.count > 1 ? 1 : 0)
if items.isEmpty { lastKnownRatio = nil }
}
.onDisappear {
itemsRevision += 1
normalize(to: items.count > 1 ? safeIndex + 1 : 0)
}
.task(id: scenePhase == .active && items.count > 1 ? items : []) {
guard scenePhase == .active, items.count > 1 else { return }
while !Task.isCancelled {
do {
try await Task.sleep(nanoseconds: Self.autoScrollNanoseconds)
} catch {
return
}
guard !Task.isCancelled else { return }
if !isSwiping { move(by: 1) }
}
}
.accessibilityAdjustableAction { direction in
switch direction {
case .increment: move(by: 1)
case .decrement: move(by: -1)
@unknown default: break
}
}
}
private var pageIndicator: some View {
HStack(spacing: 0) {
Text(String(format: "%02d", safeIndex + 1))
.foregroundColor(.white)
Text("/" + String(format: "%02d", items.count))
.foregroundColor(.gray400)
}
.appFont(.body3)
.lineLimit(1)
.fixedSize(horizontal: true, vertical: false)
.padding(.horizontal, SodaSpacing.s6)
.padding(.vertical, SodaSpacing.s4)
.background(Color.gray900)
.clipShape(Capsule())
.accessibilityHidden(true)
}
private func dragGesture(width: CGFloat) -> some Gesture {
DragGesture(minimumDistance: SodaSpacing.s12, coordinateSpace: .global)
.updating($dragOffset) { value, offset, _ in
if items.count > 1, abs(value.translation.width) > abs(value.translation.height) {
offset = value.translation.width
}
}
.updating($isSwiping) { _, swiping, _ in swiping = true }
.onEnded { value in
guard abs(value.translation.width) > abs(value.translation.height) else { return }
if value.translation.width < -width * 0.2 {
move(by: 1)
} else if value.translation.width > width * 0.2 {
move(by: -1)
}
}
}
private func move(by step: Int) {
guard items.count > 1, (1...items.count).contains(displayIndex) else { return }
let next = displayIndex + step
displayIndex = next
guard next == 0 || next == items.count + 1 else { return }
let revision = itemsRevision
DispatchQueue.main.asyncAfter(deadline: .now() + Self.transitionDuration) {
guard itemsRevision == revision, displayIndex == next else { return }
normalize(to: next == 0 ? items.count : 1)
}
}
private func normalize(to index: Int) {
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) { displayIndex = index }
}
}
private struct SubBannerTapStyle<Drag: Gesture>: PrimitiveButtonStyle {
let drag: Drag
func makeBody(configuration: Configuration) -> some View {
configuration.label
.contentShape(Rectangle())
.simultaneousGesture(drag.exclusively(before: TapGesture().onEnded { configuration.trigger() }))
.accessibilityAction { configuration.trigger() }
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,466 @@
[
{
"time": 913677.182000758,
"file": "0000.png"
},
{
"file": "0001.png",
"time": 913677.208739417
},
{
"time": 913677.243329994,
"file": "0002.png"
},
{
"file": "0003.png",
"time": 913677.2766025431
},
{
"time": 913677.3236773841,
"file": "0004.png"
},
{
"time": 913677.364179086,
"file": "0005.png"
},
{
"file": "0006.png",
"time": 913677.385782319
},
{
"time": 913677.4096495911,
"file": "0007.png"
},
{
"file": "0008.png",
"time": 913677.4411729571
},
{
"time": 913677.4755007031,
"file": "0009.png"
},
{
"time": 913677.507597494,
"file": "0010.png"
},
{
"time": 913677.541143598,
"file": "0011.png"
},
{
"time": 913677.575468246,
"file": "0012.png"
},
{
"time": 913677.607664886,
"file": "0013.png"
},
{
"file": "0014.png",
"time": 913677.63882929
},
{
"file": "0015.png",
"time": 913677.670936167
},
{
"time": 913677.704879379,
"file": "0016.png"
},
{
"file": "0017.png",
"time": 913677.7407021441
},
{
"time": 913677.770956119,
"file": "0018.png"
},
{
"file": "0019.png",
"time": 913677.805286434
},
{
"file": "0020.png",
"time": 913677.840920433
},
{
"time": 913677.8730100831,
"file": "0021.png"
},
{
"time": 913677.9581180731,
"file": "0022.png"
},
{
"file": "0023.png",
"time": 913678.00875871
},
{
"time": 913678.0434123791,
"file": "0024.png"
},
{
"file": "0025.png",
"time": 913678.073637673
},
{
"time": 913678.1048476781,
"file": "0026.png"
},
{
"file": "0027.png",
"time": 913678.143166751
},
{
"file": "0028.png",
"time": 913678.1782429271
},
{
"file": "0029.png",
"time": 913678.2079582481
},
{
"file": "0030.png",
"time": 913678.239294811
},
{
"time": 913678.272196469,
"file": "0031.png"
},
{
"time": 913678.306947525,
"file": "0032.png"
},
{
"file": "0033.png",
"time": 913678.3415255671
},
{
"file": "0034.png",
"time": 913678.372522235
},
{
"time": 913678.407802038,
"file": "0035.png"
},
{
"time": 913678.440060911,
"file": "0036.png"
},
{
"file": "0037.png",
"time": 913678.4746635171
},
{
"time": 913678.504926466,
"file": "0038.png"
},
{
"time": 913678.540071622,
"file": "0039.png"
},
{
"file": "0040.png",
"time": 913678.57196552
},
{
"file": "0041.png",
"time": 913678.608095773
},
{
"file": "0042.png",
"time": 913678.640771551
},
{
"file": "0043.png",
"time": 913678.674382659
},
{
"file": "0044.png",
"time": 913678.705199606
},
{
"time": 913678.740146241,
"file": "0045.png"
},
{
"time": 913678.771469746,
"file": "0046.png"
},
{
"file": "0047.png",
"time": 913678.8079382931
},
{
"time": 913678.837268878,
"file": "0048.png"
},
{
"time": 913678.8705325951,
"file": "0049.png"
},
{
"time": 913678.957523811,
"file": "0050.png"
},
{
"file": "0051.png",
"time": 913679.0235937741
},
{
"time": 913679.0449015361,
"file": "0052.png"
},
{
"file": "0053.png",
"time": 913679.0738721241
},
{
"file": "0054.png",
"time": 913679.1065804161
},
{
"file": "0055.png",
"time": 913679.140883742
},
{
"file": "0056.png",
"time": 913679.173805641
},
{
"time": 913679.209024106,
"file": "0057.png"
},
{
"file": "0058.png",
"time": 913679.2407425691
},
{
"time": 913679.2749094791,
"file": "0059.png"
},
{
"time": 913679.307120681,
"file": "0060.png"
},
{
"file": "0061.png",
"time": 913679.3397686051
},
{
"time": 913679.3735209911,
"file": "0062.png"
},
{
"file": "0063.png",
"time": 913679.407494329
},
{
"time": 913679.4407483081,
"file": "0064.png"
},
{
"time": 913679.4731188971,
"file": "0065.png"
},
{
"time": 913679.5077481561,
"file": "0066.png"
},
{
"file": "0067.png",
"time": 913679.557752116
},
{
"time": 913679.5799938161,
"file": "0068.png"
},
{
"file": "0069.png",
"time": 913679.6070735101
},
{
"file": "0070.png",
"time": 913679.640295565
},
{
"time": 913679.6770715461,
"file": "0071.png"
},
{
"file": "0072.png",
"time": 913679.707296167
},
{
"file": "0073.png",
"time": 913679.740801326
},
{
"file": "0074.png",
"time": 913679.7748053831
},
{
"time": 913679.8080684431,
"file": "0075.png"
},
{
"file": "0076.png",
"time": 913679.8412805101
},
{
"file": "0077.png",
"time": 913679.875727502
},
{
"file": "0078.png",
"time": 913679.9071129421
},
{
"file": "0079.png",
"time": 913679.9597379351
},
{
"time": 913679.97902312,
"file": "0080.png"
},
{
"file": "0081.png",
"time": 913680.0047622471
},
{
"file": "0082.png",
"time": 913680.042046424
},
{
"file": "0083.png",
"time": 913680.0745555841
},
{
"file": "0084.png",
"time": 913680.1082944991
},
{
"file": "0085.png",
"time": 913680.142798252
},
{
"time": 913680.173877244,
"file": "0086.png"
},
{
"time": 913680.2076699181,
"file": "0087.png"
},
{
"time": 913680.240965635,
"file": "0088.png"
},
{
"time": 913680.276695547,
"file": "0089.png"
},
{
"file": "0090.png",
"time": 913680.306939149
},
{
"file": "0091.png",
"time": 913680.339200414
},
{
"file": "0092.png",
"time": 913680.3754256241
},
{
"file": "0093.png",
"time": 913680.40727143
},
{
"time": 913680.4413317901,
"file": "0094.png"
},
{
"file": "0095.png",
"time": 913680.4747653401
},
{
"time": 913680.5317274261,
"file": "0096.png"
},
{
"file": "0097.png",
"time": 913680.5543979681
},
{
"file": "0098.png",
"time": 913680.5904828061
},
{
"file": "0099.png",
"time": 913680.6107395231
},
{
"time": 913680.6469458761,
"file": "0100.png"
},
{
"time": 913680.6707490981,
"file": "0101.png"
},
{
"file": "0102.png",
"time": 913680.724500303
},
{
"time": 913680.7592690381,
"file": "0103.png"
},
{
"file": "0104.png",
"time": 913680.7851261741
},
{
"file": "0105.png",
"time": 913680.805062163
},
{
"file": "0106.png",
"time": 913680.8376410251
},
{
"time": 913680.8706479521,
"file": "0107.png"
},
{
"file": "0108.png",
"time": 913680.9038749371
},
{
"file": "0109.png",
"time": 913680.9381074051
},
{
"file": "0110.png",
"time": 913680.987470016
},
{
"file": "0111.png",
"time": 913681.0077511901
},
{
"file": "0112.png",
"time": 913681.0417480101
},
{
"time": 913681.0748786661,
"file": "0113.png"
},
{
"time": 913681.107035094,
"file": "0114.png"
},
{
"file": "0115.png",
"time": 913681.1391082171
}
]
@@ -0,0 +1,42 @@
{
"devicesAndConfigurations" : [
{
"device" : {
"architecture" : "x86_64",
"deviceId" : "B5F2BA0D-327C-4EE3-94EE-39710A0E1ACC",
"deviceName" : "SubBannerGestureQA-20261030",
"modelName" : "iPhone SE (3rd generation)",
"osBuildNumber" : "22D8075",
"osVersion" : "18.3.1",
"platform" : "iOS Simulator"
},
"expectedFailures" : 0,
"failedTests" : 0,
"passedTests" : 11,
"skippedTests" : 0,
"testPlanConfiguration" : {
"configurationId" : "1",
"configurationName" : "Test Scheme Action"
}
}
],
"environmentDescription" : "Gesture · Built with macOS 26.0",
"expectedFailures" : 0,
"failedTests" : 0,
"finishTime" : 1793292296.135,
"passedTests" : 11,
"result" : "Passed",
"skippedTests" : 0,
"startTime" : 1793292150.956,
"statistics" : [
],
"testFailures" : [
],
"title" : "Test - Gesture",
"topInsights" : [
],
"totalTestCount" : 11
}
@@ -0,0 +1 @@
PASS: production carousel/model/UIKit opener iOS16.6 typecheck with installed Kingfisher and real theme/font files
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB