Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ScrollView scroll,decelerating - iOS SDK UIkit

0fb0b8c6eecbc8e0a3981501f6a6fea6?s=47 notoroid
September 25, 2021

ScrollView scroll,decelerating - iOS SDK UIkit

札幌iPhoneアプリ開発懇談会(Devsap) 2021年9月25日勉強会資料

0fb0b8c6eecbc8e0a3981501f6a6fea6?s=128

notoroid

September 25, 2021
Tweet

Transcript

  1. *SJNBTV%FOTBO1MBOOJOHೳొཁ 4DSPMM7JFX6*,JU 4DSPMM %FDFMFSBUJOH

  2. iOS15ެ։͞Ε·ͨ͠

  3. J04Y͔Βͷڧ੍Ξοϓσʔτ͸ͳ͍ͨΊΞοϓ σʔτ଎౓͸଎͘ͳ͍΋ͷͷɺΞϓϦ։ൃऀଆʹ ͱͬͯ͸ϓϩάϥϛϯά͠΍͘͢ͳΔ࢓૊Έ͕ಋೖ

  4. async/await/actorͷಋೖʹΑΓεϨουؒͰͷॲ ཧΛؾʹ͢Δඞཁ͕ݮΓSwiftUI(ͱ͍͏͔iOSωΠ ςΟϒΞϓϦ։ൃ)ͷֶशίετ͕Լ͕Γ·ͨ͠ɻ

  5. SwiftUI Λొ৔ͷࠒ(2019)͔ Βਪ͍ͯ͠Δ౰ํͰ͕͢ɺ

  6. ͜͜Ζ͸SwiftUIΛ཭Ε UIKit ʹ໭͍ͬͯͨɻ

  7. Կނ

  8. 6*,JUͰ΍Γ͜࢒ͨ͜͠ ͱ͕͋Δ͔Βͩ ຊԻ 4XJGU6*Ͱཉ͍͕͠4XJGU6*Ͱ࣮ݱͰ͖ͳ͍͔Β

  9. *OGJOJU-PPQ)FBEFS ແݶʹεΫϩʔϧͰ͖Δϔομ w ࠨӈํ޲ʹεΫϩʔϧՄೳ w ϝχϡʔ͕ແݶʹදࣔ͞Ε ͍ͯΔΑ͏ʹϔομ߲໨͕ εΫϩʔϧ w தԝʹ͋Δ߲໨͕બ୒த

    w ϠΫΦΫΞϓϦ ೥ ݄ݱࡏ ͷϝχϡʔ
  10. *OGJOJU-PPQ)FBEFS w ແݶϧʔϓϔομʔ͸ΦʔϓϯιʔεϕʔεͰ͍͔ͭ͘ଘࡏ w 6*$PMMFDUJPO7JFXϕʔε w %BUB4PVSDFEFMFHBUFΛ࢖ͬͯಈతʹΧϥϜੜ੒ w ਺ݸͷϝχϡʔ߲໨Λදࣔ͢Δ͚ͩͰ6*$PMMFDUJPO7JFXΛ࢖͏ඞ ཁ͕͋Δͷ͔

     w ΋ͬͱγϯϓϧʹ ͭ·Γ6*4DSPMM7JFX Ͱ࣮ݱͰ͖ͳ͍͔
  11. 6*4DSPMMʹ͍ͭͯ ߟ͑ͯΈΔ

  12. 6*4DSPMM7JFX  J04ͷ࢖͍ͪ͝͝ͷྑ͞Λܾఆ͚ͮͨ6*ύʔπ w J04 J1IPOF04 ͷࠒ͔Βଘࡏ w Ϣʔβʔͷλονૢ࡞ʹ௥ਵ͢ΔεΫϩʔϧ w

    ίϯςϯπ຤୺·Ͱ౸ୡͨ͠ࡍͷό΢ϯε w Ԡ༻6*ଟ਺ w 8,8FC7JFXɺ6*5BCMF7JFXɺ6*$PMMFDUJPO7JFX w աڈʹ͸.BQ΋࢖༻
  13. 6*4DSPMM7JFX  J04ͷ࢖͍ͪ͝͝ͷྑ͞Λܾఆ͚ͮͨ6*ύʔπ w ΞϓϦ։ൃͰ΋ෳࡶͳ࢓૊Έ w εΫϩʔϧίϯςϯπͷ෯ߴ͞ͷऔѻ͕೥ʑมԽ w "VUP4J[JOHˠ$POTUSBJOUˠ"ODIPSͱ೥ʑ੔ཧ w

    ίϯςϯπྖҬͷτϥϒϧ w J04ʙεςʔλεόʔྖҬมߋΛ΋Ζʹड͚Δ w εςʔλεόʔ͔ΒϊονྖҬ΁ͱมԽ w ෳ਺ϓϩύςΟͷ૊Έ߹ΘͤͰڍಈ͕มԽ w %FMFHBUFͰͷදࣔྖҬ੍ޚͷϊ΢ϋ΢ඞཁ
  14. 6*4DSPMM7JFXͱ೿ੜΫϥε w 6*,JU͸యܕతͳΫϥεϥ ΠϒϥϦ w ೿ੜݩΫϥεͷػೳΛ೿ੜ ݩ্͕ॻ͖͢Δ w ೿ੜ͕܁Γฦ͞ΕΔͱຊདྷ ͷػೳ͕࢖͑Δ͔೺Ѳͮ͠

    Β͘ͳΔ ೿ੜݩ ೿ੜઌ
  15. 6*4DSPMM7JFXͷػೳ্ॻ͖ঢ়گ 6*4DSPMM7JFXͷ೿ੜΫϥεͰԿ͕࢖͑Δ͔ ߟ͑Δඞཁ͋Γ

  16. 6*4DSPMM7JFXͷػೳ্ॻ͖ঢ়گ 6*4DSPMM7JFXͷ೿ੜΫϥεͰԿ͕࢖͑Δ͔ ߟ͑Δඞཁ͋Γ EFMFHBUF͸࢖༻Մೳ δΣενϟʔҎ֎͸֓Ͷ ར༻Մೳ

  17. 6*4DSPMM7JFX%FMFHBUF 6*4DSPMM7JFX಺ͰͷΠϕϯτΛัଊ͢ΔEFMFHBUF w 4DSPMMΠϕϯτ w εΫϩʔϧ։࢝ऴྃΠϕϯτ w ར༻ऀ6*ଆͷશͯͷεΫϩʔϧΠϕϯτ͕ൃੜ w ζʔϜ։࢝ऴྃΠϕϯτ

    w %FDFMFSBUJOH։࢝ऴྃΠϕϯτ w Ϣʔβʔͷૢ࡞ޙʹό΢ϯυಈ࡞ͷ։࢝ऴྃΛ௨஌ w εΫϩʔϧΛτοϓʹҠಈ͢Δ͔ͷΠϕϯτ εςʔλεྖҬͷλ οϓ
  18. 6*4DSPMM7JFX%FMFHBUF  4DSPMMͱ%FDFMFSBUJOHͷؔ܎ w 4DSPMMΠϕϯτ w Ϣʔβʔ͕λονૢ࡞Ͱ w %FDFMFSBUJOHΠϕϯτ w

    Ճ଎౓͋Γͷλονૢ࡞ޙʹɺ4DSPMM7JFX͕ຊདྷ͋Δ΂͖Ґஔ ʹίϯςϯπ͕໭Δ·ͰͷΠϕϯτ w %FDFMFSBUJOH͕ऴྃ͢Δ·Ͱը໘্ͷಈ͖͸ࢭ·Βͳ͍
  19. ͜͜·ͰΛ౿·͑ͯ ແݶϧʔϓΛ࣮ݱ͢Δ

  20. αϯϓϧίʔυ

  21. IUUQTHJUIVCDPNOPUPSPJE *OGJOJUF)FBEFS%FNP

  22. 6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶃ 4DSPMMຖʹҐஔௐ੔ɺ%FDFMFSBUJOHޙʹਖ਼نԽ

  23. 6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶄ 4DSPMMຖʹҐஔௐ੔ɺ%FDFMFSBUJOHޙʹਖ਼نԽ

  24. 6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶄ 4DSPMMຖʹҐஔௐ੔ɺ%FDFMFSBUJOHޙʹਖ਼نԽ JO fi OJUF-PPQ7JFX্ͷ4UBDL7JFXͷ9࣠Λௐ ੔ͯ͠ݶΓ͋ΔϔομཁૉΛ࠶ར༻͢Δ JO fi OJUF-PPQ7JFX͸4DSPMM7JFX੍͕ޚ͢Δ

  25. 6*4DSPMM7JFXͰແݶεΫϩʔϧΛ࣮ݱᶅ 4DSPMMຖʹҐஔௐ੔ɺ%FDFMFSBUJOHޙʹਖ਼نԽ 4DSPPM7JFXͷྖҬΛϔομʔͷΧϥϜ ෯ͱ͠ɺDMJQ5P#PVOEΛ֦͛ͯࢠཁૉΛ දࣔɺλονՄೳͳྖҬ΋֦͓͛ͯ͘

  26. 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 } }
  27. 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 } } }
  28. ·ͱΊ

  29. ·ͱΊ w4XJGU6*Ͱ࣮ݱͰ͖ͳ͍6*Λ6*,JUͰ࡞Γ·ͨ͠ wΦʔϓϯιʔεطଘͷ΋ͷΑΓγϯϓϧͳ6*ύʔπ Λ໨ࢦ͠·ͨ͠ w6*ύʔπΛ࡞੒ʹ6*4DSPMM7JFXΛ࢖༻͠·ͨ͠ w6*4DSPMM7JFXͷ4DSPMMΠϕϯτ͸Ϣʔβʔૢ࡞ɺՃ ଎౓ʹΑΔό΢ϯυؔ܎ͳ͠ʹൃੜ͠·͢ w6*4DSPMM7JFX͕׬શʹ੩ࢭ͢Δͷ͸%FDFMFSBUJOH ΠϕϯτऴྃޙͰ͢