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
350
3
Share
ScrollView scroll,decelerating - iOS SDK UIkit
札幌iPhoneアプリ開発懇談会(Devsap) 2021年9月25日勉強会資料
notoroid
September 25, 2021
More Decks by notoroid
See All by notoroid
New_features_in_Xcode26.3_to_Xcode26.4.pdf
notoroid
0
80
iOS26 オーディオ録音新機能 - iOS26 AVInputPickerInteraction
notoroid
0
98
Info.plist - after Xcode26.
notoroid
0
68
GeometryReader - SwiftUI
notoroid
0
100
iOS 26 SDKの新機能 (liquid抜き) - iOS26orLaterUpdateAndSceneForUIKit
notoroid
0
230
UIテスト自動化サポート- Testbed for XCUIAutomation practice
notoroid
0
230
UIViewController - Interactive PopGesture
notoroid
0
100
Xcodeショートカット 2025年版
notoroid
0
130
ZIPでくれ - Apple謹製 geocoding/ reverse geocoding
notoroid
0
180
Other Decks in Programming
See All in Programming
密結合なバックエンドから TypeScript のコードを生成する
kemuridama
1
370
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
1.2k
運用エージェントは "作る" から "育てる" へ - 記憶と自己進化の3層設計パターン / self-evolving-agents-three-layer-agent-design
gawa
12
3.1k
OCRを使ってゲームのアイテムをデータ化する
kishikawakatsumi
0
120
New "Type" system on PicoRuby
pocke
1
240
横断組織出身のQAEがインプロセスQAEでつまずいたこと・活かせたこと
ty89
0
430
oxlintはeslint/typescript-eslintを置き換えられるのか
shomafujita
2
270
Migrations : C'est une question d'hygiène !
vinceamstoutz
0
2.2k
技術記事、AIに書かせるか、自分で書くか? 〜それでも私が自分の手で書く理由〜 / #QiitaConference
jnchito
2
1.1k
AI 時代のソフトウェア設計の学び方
masuda220
PRO
28
10k
バックエンドにElysiaJSを採用して気付いた、良い点・悪い点
wanko_it
1
180
Claspは野良GASの夢をみるか
takter00
0
130
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
The Limits of Empathy - UXLibs8
cassininazir
1
340
Ruling the World: When Life Gets Gamed
codingconduct
0
240
Automating Front-end Workflow
addyosmani
1370
210k
Amusing Abliteration
ianozsvald
1
180
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
260
Scaling GitHub
holman
464
140k
The agentic SEO stack - context over prompts
schlessera
0
790
Tell your own story through comics
letsgokoyo
1
930
Embracing the Ebb and Flow
colly
88
5.1k
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 ΠϕϯτऴྃޙͰ͢