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

[iOS/Swift] Image Slide show - Label Page Indicator, Full screen view ๊ตฌํ˜„ํ•˜๊ธฐ

by Danna 2021. 3. 28.
728x90
728x90

๐Ÿ–ผ ImageSlideshow

ImageSlideshow ์„ค์น˜, ๋ทฐ ์ƒ์„ฑํ•˜๊ธฐ, ์ด๋ฏธ์ง€ ๋“ฑ๋กํ•˜๋Š” ๋ฒ•์€ ์ด์ „ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”. ํ•ด๋‹น ํฌ์ŠคํŒ…์—์„œ๋Š” Label Page Indicator ์ ์šฉํ•˜๋Š” ๋ฒ•, ์ด๋ฏธ์ง€ ํด๋ฆญ์‹œ ์ „์ฒด ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•˜๋Š” ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ค„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

[iOS/Swift] Image Slide show + Firebase Storage ๋กœ ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€๋ทฐ ๊ตฌํ˜„ํ•˜๊ธฐ - 1

๐Ÿ–ผ ImageSlideshow Customizable Swift image slideshow with circular scrolling, timer and full screen viewer Image Slide show ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ์˜†์œผ๋กœ ์Šฌ๋ผ์ด๋“œํ•˜๋Š” UI ๋ฅผ ๊ตฌํ˜„ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ..

jellysong.tistory.com


3. Custom Label  Page Indicator ์ ์šฉํ•˜๊ธฐ

 

๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” Label Page Indicator ๋ฅผ ์ด์šฉํ•  ๊ฒฝ์šฐ, ๊ธฐ์กด ์ฝ”๋“œ์—์„œ ๋‹ค์Œ ํ•œ ์ค„์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

slideshow.pageIndicator = LabelPageIndicator()

 

Github ๋ฌธ์„œ๋„, ์ •๋Œ€๋ฆฌ๋‹˜ ์œ ํˆฌ๋ธŒ๋„ Label Page Indicator ๋ฅผ ๋š๋”ฑ ์ ์šฉํ•˜์…จ๋Š”๋ฐ์š”. ์ €๋Š” ์ด๋ฏธ์ง€ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ž‘์•„์„œ ๊ทธ๋Ÿฐ์ง€ width ์กฐ์ ˆ์ด ์ •์ƒ์ ์œผ๋กœ ๋˜์ง€ ์•Š์•„ 1/... ์ฒ˜๋Ÿผ ์ž˜๋ ค์„œ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ๐Ÿฅฒ Label ์„ SubView ๋กœ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ํ•ด๋ดค๋Š”๋ฐ, ์Šคํฌ๋กค์‹œ ๋งค๋„๋Ÿฝ์ง€ ์•Š๋”๋ผ๊ณ ์š”. ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ extension ์„ ํ†ตํ•ด LabelPageIndicator ์˜ ํฐํŠธ์‚ฌ์ด์ฆˆ์™€ width ๋ฅผ ์ปค์Šคํ…€ํ•˜๊ฒŒ ์ง€์ •ํ•ด์คฌ์Šต๋‹ˆ๋‹ค.

 

You can also use your own page indicator by adopting the PageIndicatorView protocol.
import Foundation
import ImageSlideshow

extension LabelPageIndicator {
    
    public func setCustomLabel() {
        self.textColor = .white
        let systemFont = UIFont.systemFont(ofSize: 12.0)
        self.attributedText = NSAttributedString(string: self.text ?? "",
                                                 attributes: [NSAttributedString.Key.font : systemFont])
    }
    
    public override func sizeToFit() {
        let maximumString = String(repeating: "16", count: numberOfPages) as NSString
        self.frame.size = maximumString.size(withAttributes: [.font: font as Any])
        setCustomLabel()
    }
}

 

์ด๋ฒˆ์—๋Š” TableView Cell ์—์„œ ImageSlideshow ๋ฅผ ์ด์šฉํ–ˆ๊ณ , Cell ์—์„œ LabelPageIndicator ์„ค์ •์„ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. Cell ์˜ ๊ธฐ๋ณธ UI ์™€ ๊ด€๋ จ๋œ ๋™์ž‘์ด๋ผ ์ƒ๊ฐํ•ด์„œ Cell Class ์—์„œ ์ ์šฉ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

 class ReviewCell: UITableViewCell {
   //...
   override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code ...
        reviewImageSlide.setCornerRadius()
        let labelPageIndicator = LabelPageIndicator()
        reviewImageSlide.pageIndicator = labelPageIndicator
        reviewImageSlide.pageIndicatorPosition = .init(horizontal: .right(padding: 10),
                                                    vertical: .customBottom(padding: 10))
    }
}

 

์ด๋ฏธ์ง€๋Š” Cell ๋งˆ๋‹ค ๋™์ ์œผ๋กœ ๋ณ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— TableViewDatasource ๋‚ด์—์„œ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

// MARK: TableView - Datasource
extension ReviewTableViewController: UITableViewDataSource {
    // ...
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
		// dequeueReusableCell ... 
        let imageInputs = FirebaseStorage.getImageInputs(images: images)
        cell.reviewImageSlide.setImageInputs(imageInputs)
        cell.reviewImageSlide.contentScaleMode = .scaleAspectFill
    }
}

 

์—ฌ๊ธฐ๊นŒ์ง€ ์ž˜ ์ž‘๋™๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ค๋ฅธ์ชฝ ํ•˜๋‹จ์— Label Page Indicator ๊ฐ€ ๋‚˜ํƒ€๋‚œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ํ•ด๋‹น ์‚ฌ์ง„์€ ๊ฐœ๋ฐœ์ค‘์ธ ์–ดํ”Œ๊ณผ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค.

Custom Label Page Indicator

 

4. Full Screen view ์ „ํ™˜ํ•˜๊ธฐ + Label Page Indicator ์ ์šฉํ•˜๊ธฐ

๊ฐœ๋ฐœํ•˜๋Š” ์ •๋Œ€๋ฆฌ๋‹˜ ์œ ํˆฌ๋ธŒ๋ฅผ ์ฐธ๊ณ ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

ImageSlideshow ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ด๋ฏธ์ง€๋งŒ ๋‚˜ํƒ€๋‚˜๋Š” ์ „์ฒดํ™”๋ฉด์„ ์ œ๊ณตํ•˜๊ณ  Pinch Zoom ์„ ํ†ตํ•ด ํ™•๋Œ€๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ง์ ‘ ๊ฐœ๋ฐœํ•ด์•ผํ•˜๋Š” ๋ถ€๋ถ„์€ ์ด๋ฏธ์ง€๋ฅผ ํ„ฐ์น˜ํ–ˆ์„ ๋•Œ ์ „์ฒดํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚˜๋„๋ก ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. 

 

์ด๋ฏธ์ง€๋ฅผ ํ„ฐ์น˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ˆ๊นŒ UITapGestureRecognizer ๋ฅผ ์ด์šฉํ•˜๋ฉด ๋˜๊ฒ ์ง€์š”?! Tap Gesture ๋˜ํ•œ UI ์„ค์ •์— ๊ด€๋ จ๋œ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•ด์„œ Cell ๋‚ด๋ถ€์—์„œ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋ฅผ ํƒญ ํ–ˆ์„๋•Œ, didTapImageSlide ์•ก์…˜ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ , ํ•ด๋‹น ํ•จ์ˆ˜๋Š” showFullScreen ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

 

showFullScreen ์€ Closure ๋กœ ์„ค์ •ํ–ˆ๋Š”๋ฐ์š”, ๋ณด์—ฌ์ค˜์•ผํ•  ์ด๋ฏธ์ง€๊ฐ€ Cell ๋งˆ๋‹ค ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์— Closure ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ReviewTableViewDatasource ์—์„œ ์ˆ˜ํ–‰ํ•  ์ฝ”๋“œ๋ฅผ Closure ์— ๋„˜๊ฒจ์ฃผ๋Š” ์‹์œผ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 class ReviewCell: UITableViewCell {
    //...
    typealias EmptyClosure = (() -> ())
    var showFullScreen: EmptyClosure = {}
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapImageSlide(_:)))
        reviewImageSlide.addGestureRecognizer(tapGesture)
        // ...
    }
    
    @objc func didTapImageSlide(_ sender: UITapGestureRecognizer) {
        showFullScreen()
    }
}

 

๊ทธ๋ฆฌ๊ณ  ์ „์ฒดํ™”๋ฉด์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ, ๊ธฐ์กด์˜ LabelPageIndicator ๋Š” ๋„ˆ๋ฌด ์ž‘๊ธฐ๋•Œ๋ฌธ์— ํฐ ์‚ฌ์ด์ฆˆ์˜ Label ์„ ์ •์˜ํ•ด์คฌ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ tableView ์ฝ”๋“œ์—์„œ TopAnchor, centerXAnchor ๋ฅผ ํ†ตํ•ด ์ƒ๋‹จ ๊ฐ€์šด๋ฐ์— ์œ„์น˜์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

class ReviewCell: UITableViewCell {
    //...
    let fullScreenLabelIndicator: UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.font = UIFont.systemFont(ofSize: 16)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
}

 

ReviewTableViewDatasource ํ”„๋กœํ† ์ฝœ ํ•จ์ˆ˜์ธ tableView(_, cellForRowAt) ํ•จ์ˆ˜์—์„œ ์œ„์˜ Closure ๋ฅผ ์ง€์ •ํ•ด์ค์‹œ๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ์—์„œ๋Š” ํ˜„์žฌ ํŽ˜์ด์ง€์— ๋Œ€ํ•œ Label ๋งŒ ์ง€์ •ํ•ด์ฃผ๊ณ  ์žˆ์–ด์š”. ํŽ˜์ด์ง€๊ฐ€ ๋ณ€ํ• ๋•Œ๋งˆ๋‹ค Label ์„ ๋ณ€๊ฒฝํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ ImageSlideshowDelegate ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๊ณ  ํ•จ์ˆ˜๋ฅผ ์žฌ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

// MARK: TableView - Datasource
extension ReviewTableViewController: UITableViewDataSource {
    // ...
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    	// dequeueReusableCell ... 
        
        cell.showFullScreen = { [unowned self] in
            let fullScreenController = cell.reviewImageSlide.presentFullScreenController(from: self, completion: nil)
            fullScreenController.slideshow.pageIndicator = LabelPageIndicator()
            fullScreenController.view.addSubview(fullScreenLabelIndicator)
            fullScreenLabelIndicator.topAnchor.constraint(equalTo: fullScreenController.view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
            fullScreenLabelIndicator.centerXAnchor.constraint(equalTo: fullScreenController.view.centerXAnchor).isActive = true
            let currentPageString = String(cell.reviewImageSlide.currentPage + 1)
            let maxPageString = String(cell.reviewImageSlide.images.count)
            fullScreenLabelIndicator.text = currentPageString + "/" + maxPageString
            fullScreenController.slideshow.delegate = self
        }
    }
}
// MARK: - ImageSlideshowDelegate
extension ReviewTableViewController: ImageSlideshowDelegate {

    // ์ด๋ฏธ์ง€ ํŽ˜์ด์ง€๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„๋•Œ
    func imageSlideshow(_ imageSlideshow: ImageSlideshow, didChangeCurrentPageTo page: Int) {
        let maxPageString = String(imageSlideshow.images.count)
        fullScreenLabelIndicator.text = String(page + 1) + "/" + maxPageString
    }
}

 

์—ฌ๊ธฐ๊นŒ์ง€ ํ™•์ธํ•ด๋ณด์‹œ๋ฉด ํ„ฐ์น˜์‹œ ์ „์ฒดํ™”๋ฉด์œผ๋กœ ์ „ํ™˜๋˜๊ณ , ์ƒ๋‹จ์— Label Page Indicator ๊ฐ€ ๋‚˜ํƒ€๋‚œ ๊ฒƒ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ๐Ÿคฉ ํ•ด๋‹น ์‚ฌ์ง„์€ ๊ฐœ๋ฐœ์ค‘์ธ ์–ดํ”Œ๊ณผ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค.

Full Screen View + Labe Page Indicator


์ฐธ๊ณ  ๋งํฌ

728x90
728x90