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
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
「ブロックテーマでは再現できない」は本当か?
inc2734
0
960
Automatic Grammar Agreementと Markdown Extended Attributes について
kishikawakatsumi
0
180
360° Signals in Angular: Signal Forms with SignalStore & Resources @ngLondon 01/2026
manfredsteyer
PRO
0
120
0→1 フロントエンド開発 Tips🚀 #レバテックMeetup
bengo4com
0
560
Fluid Templating in TYPO3 14
s2b
0
130
KIKI_MBSD Cybersecurity Challenges 2025
ikema
0
1.3k
ぼくの開発環境2026
yuzneri
0
190
Rust 製のコードエディタ “Zed” を使ってみた
nearme_tech
PRO
0
160
責任感のあるCloudWatchアラームを設計しよう
akihisaikeda
3
170
QAフローを最適化し、品質水準を満たしながらリリースまでの期間を最短化する #RSGT2026
shibayu36
2
4.4k
SourceGeneratorのススメ
htkym
0
190
LLM Observabilityによる 対話型音声AIアプリケーションの安定運用
gekko0114
2
430
Featured
See All Featured
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
160
Prompt Engineering for Job Search
mfonobong
0
160
Stop Working from a Prison Cell
hatefulcrawdad
273
21k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.6k
Code Reviewing Like a Champion
maltzj
527
40k
Documentation Writing (for coders)
carmenintech
77
5.2k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
910
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
640
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.1k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
92
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
3.9k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
130
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 ΠϕϯτऴྃޙͰ͢