๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŽ iOS

[iOS/Swift] UICollectionView paging ๋œ์ปฅ ๊ฑฐ๋ฆฌ๋Š” ์ด์Šˆ ์ˆ˜์ •ํ•˜๊ธฐ

by ํ‹ด๋”” 2022. 6. 1.
728x90
๋ฐ˜์‘ํ˜•

https://www.pexels.com/ko-kr/photo/930001/

์ด์ „ ํฌ์ŠคํŒ…์—์„œ Carousel effect (UICollectionViewController paging)์„ ๊ตฌํ˜„ํ–ˆ์—ˆ์ง€๋งŒ ๊ฐœ์„ ์‚ฌํ•ญ์ด ์žˆ์—ˆ์–ด์š”! ๋ฐ”๋กœ ์ฟฐ์ฒ™๊ฑฐ๋ฆฌ๋Š” ์ด์Šˆ! ํฌ์ŠคํŒ… ์ œ๋ชฉ์„ ๋ญ๋ผ ์ง€์„ ์ง€๋„ ์• ๋งคํ•œ...ใ…Žใ…Žใ…Ž 

์ˆ˜์ • ์•ˆํ•˜๊ณ  ๊ทธ๋Œ€๋กœ ์“ฐ๊ธฐ์—๋Š” ๋งˆ์Œ์— ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค! ๋ฌด์กฐ๊ฑด ์‚ฌ์šฉ์ž๊ฐ€ UI/UX์— ๋ถ€๋“œ๋Ÿฌ์›€์„ ๋Š๋ผ๊ฒŒ ํ•ด์ฃผ๊ณ  ์‹ถ๋‹จ ๋ง์ด์ฅฌ ๐Ÿค”

 

์—ฌํ•˜ํŠผ ์ˆ˜์ •ํ•ด ๋ด…์‹œ๋‹ค ์™„์„ฑ๋œ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ ๊นƒํ—ˆ๋ธŒ์—์„œ ๋‹ค์šด ๋ฐ›์œผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค-> ๊นƒํ—ˆ๋ธŒ๋งํฌ


์šฐ์„  ์ด์ „ ํฌ์ŠคํŒ…์—์„œ ์ž‘์„ฑํ•ด ๋‘์—ˆ๋˜ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์™€์„œ ๋กœ๊ทธ๋ฅผ ์œ„ํ•ด print ๋ฌธ์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
            return
        }
        
        let contentLeftInset = scrollView.contentInset.left
        let cellWithSpacing = layout.itemSize.width + layout.minimumLineSpacing
        print("ํƒ€๊ฒŸ ์˜คํ”„์…‹ ::: ", targetContentOffset.pointee.x)
        print("์Šคํฌ๋กค๋ทฐ ::: ", scrollView.contentOffset.x)
        
        
        let index = (targetContentOffset.pointee.x + contentLeftInset) / cellWithSpacing
        let roundedIndex: CGFloat = round(index)

        targetContentOffset.pointee = CGPoint(x: roundedIndex * cellWithSpacing - contentLeftInset, y: scrollView.contentInset.top)
        print("์ตœ์ข… ::: ", targetContentOffset.pointee.x)
        print("\n")
    }

์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋Š” 

์›๋ž˜ ์ด๋™ํ•˜๋ ค๊ณ  ํ–ˆ๋˜ ์˜คํ”„์…‹ ๊ฐ’ targetContentOffset, ์Šคํฌ๋กค ๋ทฐ์˜ ์†์„ฑ, velocity ์ด๋ ‡๊ฒŒ ์žˆ๋Š”๋ฐ ํ•œ๋ฒˆ ํ•ด๊ฒฐํ•ด ๋ด…์‹œ๋‹ค

contentOffset ๋ผ๋ฆฌ ๋น„๊ตํ•ด ๋ณด๋ฉด ๋ญ๊ฐ€ ๋‚˜์˜ฌ ๊ฒƒ ๊ฐ™์€๋ฐ ๋ง์ด์ฃ 

 

์™ผ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ€๋•Œ (์ปจํ…์ธ  ๋” ๋กœ๋”ฉ ํ•˜๋Š” ๋ฐฉํ–ฅ)

์ฟฐ์ฒ™ : ์›๋ž˜ ๊ฐ€๋ ค๋˜ ํƒ€๊ฒŸ ์˜คํ”„์…‹ > ์Šคํฌ๋กค ๋ทฐ ์œ„์น˜ 

 

์ •์ƒ : ํƒ€๊ฒŸ์˜คํ”„์…‹ 225 > ์Šคํฌ๋กค ๋ทฐ 78

 

 

 

์ด์ „ ์ปจํ…์ธ ๋ฅผ ๋ณด๋ ค๊ณ  ํ•  ๋•Œ 

์ฟฐ์ฒ™ : ๊ฐ€๋ ค๋˜ ํƒ€๊ฒŸ ์˜คํ”„์…‹ < ์Šคํฌ๋กค ๋ทฐ ์œ„์น˜

์ •์ƒ : ํƒ€๊ฒŸ ์˜คํ”„์…‹ < ์Šคํฌ๋กค ๋ทฐ

 

=> ์—ฌ๊ธฐ์„œ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

์ปจํ…์ธ ๋ฅผ ์™ผ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ๋ฏธ๋Š” ๊ฒฝ์šฐ - ์ดํ•˜ ์ƒˆ์ปจํ…์ธ  ๋ฐฉํ–ฅ : targetContentOffset.pointee.x > scrollView.contentOffset.x

์ปจํ…์ธ ๋ฅผ ์˜ค๋ฅธ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ๋ฏธ๋Š” ๊ฒฝ์šฐ - ์ดํ•˜ ์ด์ „ ์ปจํ…์ธ  ๋ฐฉํ–ฅ : targetContentOffset.pointee.x < scrollView.contentOffset.x 

์กฐ๊ฑด์‹์„ ํ•˜๋‚˜ ์•Œ์•˜์œผ๋‹ˆ ์ ์–ด ์ค์‹œ๋‹ค

        if scrollView.contentOffset.x < targetContentOffset.pointee.x {
            // ์™ผ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ƒˆ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
        } else {
            // ์˜ค๋ฅธ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ด์ „ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
        }

๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•˜๋‚˜ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์žˆ์–ด์š”!

๋‘๊ฐœ ๋ชจ๋‘ 0๋ฒˆ์žฌ ์…€์—์„œ 1๋ฒˆ์งธ ์…€ ๋กœ ์ด๋™ํ•˜๋Š” ๊ฑฐ์—์š”! ์™ผ์ชฝ์€ ๋œ์ปฅ ๊ฑฐ๋ฆด ๋•Œ, ์˜ค๋ฅธ์ชฝ์€ ์ œ๋Œ€๋กœ ํŽ˜์ด์ง•์ด ๋˜์—ˆ์„ ๋•Œ

targetContentOffset์˜ ๊ฒฝ์šฐ ๋น ๋ฅด๊ฒŒ ์Šคํฌ๋กค ํ•  ๊ฒฝ์šฐ ๋” ์›€์ง์ž„์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ’์ด ํŠ€์–ด์„œ ๋น„๊ตํ•˜๊ธฐ ์• ๋งคํ•˜์ง€๋งŒ

์Šคํฌ๋กค ๋ทฐ์˜ ์ปจํ…์ธ  ์˜คํ”„์…‹ scrollView.contentOffset.x ์™€ ํŽ˜์ด์ง•์ด ์ ์šฉ๋˜์–ด ๋ฉˆ์ถ”๋Š” ์ง€์ ์ด ๋ฐ”๋€ targetContentOffset.pointte.x๋ฅผ ๋น„๊ตํ•ด ์ค„๊ฒŒ์š”!

 

์™ผ์ชฝ ๋ถ€ํ„ฐ ๋ด…์‹œ๋‹น! ์›๋ž˜ ๊ฐ€๋ ค๊ณ  ํ–ˆ๋˜ ๋ฐฉํ–ฅ์€ + ๊ฐ€ ๋˜๋Š” ๋ฐฉํ–ฅ์ด์ง€๋งŒ ์ตœ์ข… ์ ์œผ๋กœ ๋ฉˆ์ถ˜ ํฌ์ธํŠธ๋Š” ๊ฐ€๋ ค๊ณ  ํ–ˆ๋˜ ๋ฐฉํ–ฅ๋ณด๋‹ค ์ ์Šต๋‹ˆ๋‹ค

๊ทธ๋Ÿผ ์ •์ƒ์ ์œผ๋กœ ๊ฐ”์„ ๋•Œ๋Š” ์Šคํฌ๋กค ๋˜์—ˆ๋˜ ์ง€์ ๋ณด๋‹ค ํ›จ์”ฌ ์•ž์„  ๋ฐฉํ–ฅ์ž…๋‹ˆ๋‹ค

๋งŒ์•ฝ 1๋ฒˆ์งธ์—์„œ 0๋ฒˆ์งธ๋กœ ์ด๋™ํ•  ๋•Œ๋Š” ์ด์™€ ๋ฐ˜๋Œ€๋กœ ๋‚˜ํƒ€๋‚˜๊ฒ ์ฃ ?

 

์กฐ๊ฑด์‹์„ ํ•˜๋‚˜ ๋” ์ ์–ด ์ค๋‹ˆ๋‹ค.

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
            return
        }
        
        let contentLeftInset = scrollView.contentInset.left
        let cellWithSpacing = layout.itemSize.width + layout.minimumLineSpacing
        
        let index = (targetContentOffset.pointee.x + contentLeftInset) / cellWithSpacing
        let roundedIndex: CGFloat = round(index)

        let adjustOffsetX = roundedIndex * cellWithSpacing - contentLeftInset
        
        if scrollView.contentOffset.x < targetContentOffset.pointee.x {
            // ์™ผ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ƒˆ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
            if scrollView.contentOffset.x > adjustOffsetX {
                print("๐Ÿ‘ˆ๐Ÿšจ")
            } else {
                print("๐Ÿ‘ˆ๐ŸŒผ")
            }
        } else {
            // ์˜ค๋ฅธ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ด์ „ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
            if scrollView.contentOffset.x < adjustOffsetX {
                print("๐Ÿ‘‰๐Ÿšจ")
            } else {
                print("๐Ÿ‘‰๐ŸŒผ")
            }
        }
        
        targetContentOffset.pointee = CGPoint(x: roundedIndex * cellWithSpacing - contentLeftInset, y: scrollView.contentInset.top)
    }

์ด๋•Œ ์ฃผ์˜ ํ•ด์•ผ ํ•  ๊ฒƒ์€ ์ตœ์ข… ํฌ์ธํŠธ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ  ๋ฐ”๋กœ ๋Œ€์ž…ํ•˜์ง€ ๋ง๊ณ  ์กฐ๊ฑด๋ฌธ์„ ํƒ„ ๋‹ค์Œ targetContentOffset.pointee์— ์ ์šฉํ•ด ์ฃผ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์•ผ ์ฒ˜์Œ์— ์ ์–ด์ค€ ์กฐ๊ฑด๋ฌธ(scrollView.contentOffset.x < targetContentOffset.pointee.x)์ด ์ œ๋Œ€๋กœ ๋™์ž‘ํ•ด์š”!

 

gif๋กœ ๋ฐ”๊พธ๋‹ˆ๊นŒ ์ข€ ๋Š๋ ค์กŒ๋Š”๋ฐ ๋œ์ปฅ ๊ฑฐ๋ฆฌ๋ฉด๐Ÿšจ์ด ์ฐํžˆ๊ณ  ์ •์ƒ์ ์œผ๋กœ ์ด๋™ํ•˜๊ฑฐ๋‚˜ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋„˜์–ด๊ฐ€๋ฉด ๐ŸŒผ ์ž…๋‹ˆ๋‹น

        if scrollView.contentOffset.x < targetContentOffset.pointee.x {
            // ์™ผ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ƒˆ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
            if scrollView.contentOffset.x > adjustOffsetX {
                print("๐Ÿ‘ˆ๐Ÿšจ")
            } else {
                targetContentOffset.pointee = CGPoint(x: adjustOffsetX, y: scrollView.contentInset.top)
            }
        } else {
            // ์˜ค๋ฅธ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ด์ „ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
            if scrollView.contentOffset.x < adjustOffsetX {
                print("๐Ÿ‘‰๐Ÿšจ")
            } else {
                targetContentOffset.pointee = CGPoint(x: adjustOffsetX, y: scrollView.contentInset.top)
            }
        }

์šฐ์„  ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ณณ์— targetContentOffset์„ ๋ณ€๊ฒฝํ•˜์—ฌ ์ •์ƒ์ ์ธ ํŽ˜์ด์ง•์ด ๋˜๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค

์ด์ œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋ด…์‹œ๋‹ซ ๋œ์ปฅ ๊ฑฐ๋ฆด ๋•Œ targetContentOffset ์„ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ  

targetContentOffset.pointee = scrollView.contentOffset

์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ์ฃผ๋ฉด ์†๊ฐ€๋ฝ์„ ๋–ผ๊ณ  ์Šคํฌ๋กค์ด ๋ฉˆ์ถ˜ ์ง€์ ์ด ์ตœ์ข… ์ง€์ ์ด ๋ ๊ฑฐ์—์š”!

์›๋ž˜๋Š” ํ‹ฑํ‹ฑ ๋˜์—ˆ์–ด์•ผ ํ•  ์ƒํ™ฉ์—์„œ ์Šคํฌ๋กค ๋งˆ์ง€๋ง‰ ์ง€์ ์—์„œ ๋ฉˆ์ถ˜๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ์ด์ œ ํ• ๊ฒƒ์€ ๋ฌด์—‡? ์›๋ž˜ ํŽ˜์ด์ง• ๋  ๊ณณ์œผ๋กœ ๋ถ€๋“œ~๋Ÿฝ๊ฒŒ ๋„˜์–ด๊ฐ€๋ฉด ๋˜๊ฒ ์ฅฌ??!!! 

 open func setContentOffset(_ contentOffset: CGPoint, animated: Bool)

์• ๋‹ˆ๋ฉ”์ด์…˜ ๋„ฃ์–ด์ค˜๋„ ๋˜๊ฒ ์ง€๋งŒ ์›๋ž˜ ์žˆ๋Š” api๋ฅผ ์‚ฌ์šฉํ•ด ๋ด…์‹œ๋‹ค! contentOffset์„ ์ง€์ •ํ•˜๊ณ   animated๋ฅผ ํ†ตํ•ด์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์œ ๋ฌด๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”!

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
            return
        }
        
        let contentLeftInset = scrollView.contentInset.left
        let cellWithSpacing = layout.itemSize.width + layout.minimumLineSpacing
        
        let index = (targetContentOffset.pointee.x + contentLeftInset) / cellWithSpacing
        let roundedIndex: CGFloat = round(index)

        let adjustOffsetX = roundedIndex * cellWithSpacing - contentLeftInset
        let adjustCGPoint = CGPoint(x: adjustOffsetX, y: scrollView.contentInset.top)
        
        if scrollView.contentOffset.x < targetContentOffset.pointee.x {
            // ์™ผ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ƒˆ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
            if scrollView.contentOffset.x > adjustOffsetX {
                targetContentOffset.pointee = scrollView.contentOffset
                scrollView.setContentOffset(adjustCGPoint, animated: true)
            } else {
                targetContentOffset.pointee = adjustCGPoint
            }
        } else {
            // ์˜ค๋ฅธ์ชฝ ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ - ์ด์ „ ์ปจํ…์ธ  ๋ฐฉํ–ฅ
            if scrollView.contentOffset.x < adjustOffsetX {
                targetContentOffset.pointee = scrollView.contentOffset
                scrollView.setContentOffset(adjustCGPoint, animated: true)
            } else {
                targetContentOffset.pointee = adjustCGPoint
            }
        }

    }

๐Ÿšจ ์›๋ž˜ ํ‹ฑํ‹ฑ ๊ฑฐ๋ฆฌ๋˜ ํŽ˜์ด์ง•

๐ŸŒผ ์ˆ˜์ •๋œ ํŽ˜์ด์ง•

์ง„์งœ ์ง์ ‘ ์Šคํฌ๋กค ํ•ด๋ณด๋ฉด ์ˆ˜์ •๋œ ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๋ถ€๋“œ๋Ÿฌ์›Œ์š”!!!

๐Ÿฆ‘ ์ด์ƒํ•œ ์ ์ด๋‚˜ ์ž˜ ๋ชป๋œ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ๋Œ“๊ธ€๋กœ ๊ณต์œ ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!!

์ถœ์ฒ˜ 

iOS_๋งˆ์Šคํ„ฐ_์ฒœ์žฌ_์ง์žฅ_๋™๋ฃŒ

์ฝ”๋“œ ์˜ฎ๊ธฐ์‹ค ๋•Œ ์ถœ์ฒ˜ ๋ฐํ˜€ ์ฃผ์„ธ์šฉ  ๐Œ… เ … ‎๐จ› ๐Œ… เ … ๐จ› ๐Œ… เ … ๐จ› 

728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€