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
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
notoroid
September 25, 2021
Programming
350
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ScrollView scroll,decelerating - iOS SDK UIkit
札幌iPhoneアプリ開発懇談会(Devsap) 2021年9月25日勉強会資料
notoroid
September 25, 2021
More Decks by notoroid
See All by notoroid
Testflight - iOS27 でのAppleIntelligenceの強化
notoroid
0
47
New_features_in_Xcode26.3_to_Xcode26.4.pdf
notoroid
0
94
iOS26 オーディオ録音新機能 - iOS26 AVInputPickerInteraction
notoroid
0
130
Info.plist - after Xcode26.
notoroid
0
76
GeometryReader - SwiftUI
notoroid
0
110
iOS 26 SDKの新機能 (liquid抜き) - iOS26orLaterUpdateAndSceneForUIKit
notoroid
0
250
UIテスト自動化サポート- Testbed for XCUIAutomation practice
notoroid
0
240
UIViewController - Interactive PopGesture
notoroid
0
110
Xcodeショートカット 2025年版
notoroid
0
140
Other Decks in Programming
See All in Programming
信頼性について考えてみる(SRE NEXT 2026 miniLT)
hayama17
0
240
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
130
AI時代の仕事技芸論〜ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ(スクフェス仙台 2026バージョン)
kuranuki
0
800
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
580
作るコストが小さくなった時代 幸せに働くために改めて考えたいこと 〜エンジニアとして価値を出し続けるために注視している二分野〜
yuppeeng
0
180
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
190
Apache Hive: そしてCloud Native Lakehouseへ
okumin
1
200
Android CLI
fornewid
0
200
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
430
TSX の <Hoge<Fuga>> という構文に驚いた話 / tsx-type-argument-syntax
kanaru0928
0
190
AI時代に設計が 最大の生産性レバーになる 意図駆動開発とデータを消さない設計|Don't Delete Your Data or Your Intent — Design as the Deepest Lever in the AI Era
tomohisa
0
380
komatsuna「分散システムにおけるバグ分析手法」
komatsunaqa
0
150
Featured
See All Featured
New Earth Scene 8
popppiees
3
2.4k
Ruling the World: When Life Gets Gamed
codingconduct
0
290
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Product Roadmaps are Hard
iamctodd
55
12k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
650
How to make the Groovebox
asonas
2
2.3k
Evolving SEO for Evolving Search Engines
ryanjones
0
250
HDC tutorial
michielstock
2
770
Leo the Paperboy
mayatellez
8
2.1k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
760
The agentic SEO stack - context over prompts
schlessera
0
860
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 ΠϕϯτऴྃޙͰ͢