Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ScrollView scroll,decelerating - iOS SDK UIkit
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
notoroid
September 25, 2021
Programming
3
330
ScrollView scroll,decelerating - iOS SDK UIkit
札幌iPhoneアプリ開発懇談会(Devsap) 2021年9月25日勉強会資料
notoroid
September 25, 2021
Tweet
Share
More Decks by notoroid
See All by notoroid
iOS26 オーディオ録音新機能 - iOS26 AVInputPickerInteraction
notoroid
0
70
Info.plist - after Xcode26.
notoroid
0
42
GeometryReader - SwiftUI
notoroid
0
79
iOS 26 SDKの新機能 (liquid抜き) - iOS26orLaterUpdateAndSceneForUIKit
notoroid
0
190
UIテスト自動化サポート- Testbed for XCUIAutomation practice
notoroid
0
210
UIViewController - Interactive PopGesture
notoroid
0
74
Xcodeショートカット 2025年版
notoroid
0
100
ZIPでくれ - Apple謹製 geocoding/ reverse geocoding
notoroid
0
150
WeatherKit iOS18 update - お天気の更新
notoroid
0
110
Other Decks in Programming
See All in Programming
AgentCoreとHuman in the Loop
har1101
5
230
KIKI_MBSD Cybersecurity Challenges 2025
ikema
0
1.3k
そのAIレビュー、レビューしてますか? / Are you reviewing those AI reviews?
rkaga
6
4.5k
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
380
Apache Iceberg V3 and migration to V3
tomtanaka
0
160
15年続くIoTサービスのSREエンジニアが挑む分散トレーシング導入
melonps
2
190
React 19でつくる「気持ちいいUI」- 楽観的UIのすすめ
himorishige
11
7.4k
例外処理とどう使い分ける?Result型を使ったエラー設計 #burikaigi
kajitack
16
6k
Data-Centric Kaggle
isax1015
2
770
CSC307 Lecture 04
javiergs
PRO
0
660
CSC307 Lecture 08
javiergs
PRO
0
670
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
7
2.3k
Featured
See All Featured
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
110
Context Engineering - Making Every Token Count
addyosmani
9
650
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
62
49k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
750
What does AI have to do with Human Rights?
axbom
PRO
0
2k
Code Reviewing Like a Champion
maltzj
527
40k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
270
First, design no harm
axbom
PRO
2
1.1k
Deep Space Network (abreviated)
tonyrice
0
47
VelocityConf: Rendering Performance Case Studies
addyosmani
333
24k
What's in a price? How to price your products and services
michaelherold
247
13k
Docker and Python
trallard
47
3.7k
Transcript
*SJNBTV%FOTBO1MBOOJOHೳొཁ 4DSPMM7JFX6*,JU 4DSPMM %FDFMFSBUJOH
iOS15ެ։͞Ε·ͨ͠
J04Y͔Βͷڧ੍Ξοϓσʔτͳ͍ͨΊΞοϓ σʔτ͘ͳ͍ͷͷɺΞϓϦ։ൃऀଆʹ ͱͬͯϓϩάϥϛϯά͘͢͠ͳΔΈ͕ಋೖ
async/await/actorͷಋೖʹΑΓεϨουؒͰͷॲ ཧΛؾʹ͢Δඞཁ͕ݮΓSwiftUI(ͱ͍͏͔iOSωΠ ςΟϒΞϓϦ։ൃ)ͷֶशίετ͕Լ͕Γ·ͨ͠ɻ
SwiftUI Λొͷࠒ(2019)͔ Βਪ͍ͯ͠ΔํͰ͕͢ɺ
͜͜ΖSwiftUIΛΕ UIKit ʹ͍ͬͯͨɻ
Կނ
6*,JUͰΓͨ͜͜͠ ͱ͕͋Δ͔Βͩ ຊԻ 4XJGU6*Ͱཉ͍͕͠4XJGU6*Ͱ࣮ݱͰ͖ͳ͍͔Β
*OGJOJU-PPQ)FBEFS ແݶʹεΫϩʔϧͰ͖Δϔομ w ࠨӈํʹεΫϩʔϧՄೳ w ϝχϡʔ͕ແݶʹදࣔ͞Ε ͍ͯΔΑ͏ʹϔομ߲͕ εΫϩʔϧ w தԝʹ͋Δ߲͕બத
w ϠΫΦΫΞϓϦ ݄ݱࡏ ͷϝχϡʔ
*OGJOJU-PPQ)FBEFS w ແݶϧʔϓϔομʔΦʔϓϯιʔεϕʔεͰ͍͔ͭ͘ଘࡏ w 6*$PMMFDUJPO7JFXϕʔε w %BUB4PVSDFEFMFHBUFΛͬͯಈతʹΧϥϜੜ w ݸͷϝχϡʔ߲Λදࣔ͢Δ͚ͩͰ6*$PMMFDUJPO7JFXΛ͏ඞ ཁ͕͋Δͷ͔
w ͬͱγϯϓϧʹ ͭ·Γ6*4DSPMM7JFX Ͱ࣮ݱͰ͖ͳ͍͔
6*4DSPMMʹ͍ͭͯ ߟ͑ͯΈΔ
6*4DSPMM7JFX J04ͷ͍ͪ͝͝ͷྑ͞Λܾఆ͚ͮͨ6*ύʔπ w J04 J1IPOF04 ͷࠒ͔Βଘࡏ w Ϣʔβʔͷλονૢ࡞ʹਵ͢ΔεΫϩʔϧ w
ίϯςϯπ·Ͱ౸ୡͨ͠ࡍͷόϯε w Ԡ༻6*ଟ w 8,8FC7JFXɺ6*5BCMF7JFXɺ6*$PMMFDUJPO7JFX w աڈʹ.BQ༻
6*4DSPMM7JFX J04ͷ͍ͪ͝͝ͷྑ͞Λܾఆ͚ͮͨ6*ύʔπ w ΞϓϦ։ൃͰෳࡶͳΈ w εΫϩʔϧίϯςϯπͷ෯ߴ͞ͷऔѻ͕ʑมԽ w "VUP4J[JOHˠ$POTUSBJOUˠ"ODIPSͱʑཧ w
ίϯςϯπྖҬͷτϥϒϧ w J04ʙεςʔλεόʔྖҬมߋΛΖʹड͚Δ w εςʔλεόʔ͔ΒϊονྖҬͱมԽ w ෳϓϩύςΟͷΈ߹ΘͤͰڍಈ͕มԽ w %FMFHBUFͰͷදࣔྖҬ੍ޚͷϊϋඞཁ
6*4DSPMM7JFXͱੜΫϥε w 6*,JUయܕతͳΫϥεϥ ΠϒϥϦ w ੜݩΫϥεͷػೳΛੜ ݩ্͕ॻ͖͢Δ w ੜ͕܁Γฦ͞ΕΔͱຊདྷ ͷػೳ͕͑Δ͔Ѳͮ͠
Β͘ͳΔ ੜݩ ੜઌ
6*4DSPMM7JFXͷػೳ্ॻ͖ঢ়گ 6*4DSPMM7JFXͷੜΫϥεͰԿ͕͑Δ͔ ߟ͑Δඞཁ͋Γ
6*4DSPMM7JFXͷػೳ্ॻ͖ঢ়گ 6*4DSPMM7JFXͷੜΫϥεͰԿ͕͑Δ͔ ߟ͑Δඞཁ͋Γ EFMFHBUF༻Մೳ δΣενϟʔҎ֎֓Ͷ ར༻Մೳ
6*4DSPMM7JFX%FMFHBUF 6*4DSPMM7JFXͰͷΠϕϯτΛัଊ͢ΔEFMFHBUF w 4DSPMMΠϕϯτ w εΫϩʔϧ։࢝ऴྃΠϕϯτ w ར༻ऀ6*ଆͷશͯͷεΫϩʔϧΠϕϯτ͕ൃੜ w ζʔϜ։࢝ऴྃΠϕϯτ
w %FDFMFSBUJOH։࢝ऴྃΠϕϯτ w Ϣʔβʔͷૢ࡞ޙʹόϯυಈ࡞ͷ։࢝ऴྃΛ௨ w εΫϩʔϧΛτοϓʹҠಈ͢Δ͔ͷΠϕϯτ εςʔλεྖҬͷλ οϓ
6*4DSPMM7JFX%FMFHBUF 4DSPMMͱ%FDFMFSBUJOHͷؔ w 4DSPMMΠϕϯτ w Ϣʔβʔ͕λονૢ࡞Ͱ w %FDFMFSBUJOHΠϕϯτ w
Ճ͋Γͷλονૢ࡞ޙʹɺ4DSPMM7JFX͕ຊདྷ͋Δ͖Ґஔ ʹίϯςϯπ͕Δ·ͰͷΠϕϯτ w %FDFMFSBUJOH͕ऴྃ͢Δ·Ͱը໘্ͷಈ͖ࢭ·Βͳ͍
͜͜·ͰΛ౿·͑ͯ ແݶϧʔϓΛ࣮ݱ͢Δ
αϯϓϧίʔυ
IUUQTHJUIVCDPNOPUPSPJE *OGJOJUF)FBEFS%FNP
6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶃ 4DSPMMຖʹҐஔௐɺ%FDFMFSBUJOHޙʹਖ਼نԽ
6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶄ 4DSPMMຖʹҐஔௐɺ%FDFMFSBUJOHޙʹਖ਼نԽ
6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶄ 4DSPMMຖʹҐஔௐɺ%FDFMFSBUJOHޙʹਖ਼نԽ JO fi OJUF-PPQ7JFX্ͷ4UBDL7JFXͷ9࣠Λௐ ͯ͠ݶΓ͋ΔϔομཁૉΛ࠶ར༻͢Δ JO fi OJUF-PPQ7JFX4DSPMM7JFX੍͕ޚ͢Δ
6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶅ 4DSPMMຖʹҐஔௐɺ%FDFMFSBUJOHޙʹਖ਼نԽ 4DSPPM7JFXͷྖҬΛϔομʔͷΧϥϜ ෯ͱ͠ɺDMJQ5P#PVOEΛ֦͛ͯࢠཁૉΛ දࣔɺλονՄೳͳྖҬ֦͓͛ͯ͘
extension InfiniteLoopHeaderView: UIScrollViewDelegate { … // DeceleratingͷରԠ func scrollViewDidEndDecelerating(_ scrollView:
UIScrollView) { // εΫϩʔϧҐஔͷਖ਼نԽ͕ߦΘΕ͍ͯͳ͍߹ guard scrollNormalizedPosition != 0 else { return } // ίϯςϯπͷதԝҐஔΛऔಘ let scrollViewCenter = scrollView.superview!.convert(scrollView.center, to: contentView) // தԝҐஔؚ͕·ΕΔۣܗใΛݩʹϔομʔཁૉͷΠϯσοΫεΛܭࢉ var targetIndex: Int = -1 for index in 0..<(leftOverrun + elementCount + rightOverrun) { let hitTestRect = CGRect(origin: CGPoint(x: CGFloat(index) * scrollView.bounds.width, y: 0), size: scrollView.bounds.size) if hitTestRect.contains(scrollViewCenter) { targetIndex = index } } // ಘΒΕͨΠϯσοΫεΛݩʹΠϯσοΫεใΛਖ਼نԽ let position = targetIndex - leftOverrun let loopIndex = (elementCount + ( (position) % elementCount)) % elementCount selectedIndex = loopIndex // ͜͜ͰεΫϩʔϧॲཧΛϓϩςΫτ͢Δ(scrollNormalizedPosition͕มߋ͞Εͯ͠·͏ՄೳੑΛආ͚ΔͨΊ) skipDidScroll = true self.scrollView.contentOffset = CGPoint(x: self.scrollView.bounds.width * CGFloat(centerForFiniteLoop() + selectedIndex), y: 0) skipDidScroll = false scrollNormalizedPosition = 0 centerXConstraint.constant = 0 } }
extension InfiniteLoopHeaderView: UIScrollViewDelegate { … // εΫϩʔϧͷରԠ func scrollViewDidScroll(_ scrollView:
UIScrollView) { // skipDidScroll͞Ε͍ͯΔ߹scrollViewDidEndDecelerating Ͱͷ guard skipDidScroll != true else { return } // ScrollViewͷcontentOffsetͱscrollNormalizedPosition͔ΒҐஔΛಘΔ let plainPosition = Int(ceil( (scrollView.contentOffset.x / scrollView.bounds.width) - CGFloat(centerForFiniteLoop()) + CGFloat(scrollNormalizedPosition) ) ) if plainPosition <= leftSafeArea { // ϔομʔࠨͷ҆શྖҬΛ͍͑ͯͨ߹scrollNormalizedPosition ʹҐஔΛ֨ೲ͠contentView ͷҐஔΛͣΒ͢ scrollNormalizedPosition = scrollNormalizedPosition + -plainPosition + (elementCount - visibleColumnNumber) centerXConstraint.constant = CGFloat(-scrollNormalizedPosition) * scrollView.bounds.width } else if plainPosition >= rightSafeArea { // ϔομʔӈͷ҆શྖҬΛ͍͑ͯͨ߹scrollNormalizedPosition ʹҐஔΛ֨ೲ͠contentView ͷҐஔΛͣΒ͢ scrollNormalizedPosition = scrollNormalizedPosition + -plainPosition centerXConstraint.constant = CGFloat(-scrollNormalizedPosition) * scrollView.bounds.width } else { // ͦͷଞͷ߹௨ৗॲཧɻબΠϯσοΫεΛมߋ let lIndex = (elementCount + (plainPosition % elementCount)) % elementCount selectedIndex = lIndex } } }
·ͱΊ
·ͱΊ w4XJGU6*Ͱ࣮ݱͰ͖ͳ͍6*Λ6*,JUͰ࡞Γ·ͨ͠ wΦʔϓϯιʔεطଘͷͷΑΓγϯϓϧͳ6*ύʔπ Λࢦ͠·ͨ͠ w6*ύʔπΛ࡞ʹ6*4DSPMM7JFXΛ༻͠·ͨ͠ w6*4DSPMM7JFXͷ4DSPMMΠϕϯτϢʔβʔૢ࡞ɺՃ ʹΑΔόϯυؔͳ͠ʹൃੜ͠·͢ w6*4DSPMM7JFX͕શʹ੩ࢭ͢Δͷ%FDFMFSBUJOH ΠϕϯτऴྃޙͰ͢