Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] SkeletonView 스켈레톤 뷰 (로딩 뷰) 본문

iOS framework

[iOS - swift] SkeletonView 스켈레톤 뷰 (로딩 뷰)

jake-kim 2022. 1. 19. 23:58

label 2개 skeletonView 적용

프레임워크

  • SkeletonView
    pod 'SkeletonView'​
  • 코드로 UI 작성에 편리를 위해 사용한 프레임워크
    pod 'Then'
    pod 'SnapKit'​

Label 2개에 SkeletonView 적용 방법

  • 적용하려는 View에 isSkeletonView = true로 skeleton 활성화
    import SkeletonView
    
    ...
    
    label1.isSkeletonable = true
    label2.isSkeletonable = true​
  • label1과 label2의 superView에도 isSkeletonable = true 활성화
    self.view.isSkeletonable = true​
     
  • SkeletonView 프레임워크는 superview.showSkeleton()하면 subview들도 모두 skeleton이 동작하므로, 아래 코드로 skeleton 실행
    self.view.showSkeleton()​
     
  • skeleton의 종류
    // https://swiftobc.com/repo/Juanpe-SkeletonView-swift-ui
    
    (1) view.showSkeleton()                 // Solid
    (2) view.showGradientSkeleton()         // Gradient
    (3) view.showAnimatedSkeleton()         // Solid animated
    (4) view.showAnimatedGradientSkeleton() // Gradient animated​


  • skeleton을 종료하고 싶은 경우 stopSkeleton()
    self.view.showSkeleton()
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
      self.view.hideSkeleton()
    }​

skeleton 

  • 애니메이션을 주고 싶은 경우,SkeletonAnimationBuilder() 객체 사용
    • showAnimatedGradientSkeleton에서 transition값은 스켈레톤 애니메이션이 보여질때와 사라질때의 transition 정의
      let skeletonAnimation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftRight)
      self.view.showAnimatedGradientSkeleton(usingGradient: .init(colors: [.lightGray, .gray]), animation: skeletonAnimation, transition: .none)
      DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
        self.view.hideSkeleton()
      }

적용된 애니메이션

cf) withDirection에 들어갈 수 있는 값

 

https://swiftobc.com/repo/Juanpe-SkeletonView-swift-ui

TableView의 Cell 내용에 Skeleton 적용 방법

  • UITableViewDataSource의 성격을 가지고 있는 SkeletonTableViewDataSource 준수하여 DataSource 완성
    public protocol SkeletonTableViewDataSource: UITableViewDataSource {
        func numSections(in collectionSkeletonView: UITableView) -> Int // Default: 1
        func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int
        func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier
        func collectionSkeletonView(_ skeletonView: UITableView, skeletonCellForRowAt indexPath: IndexPath) -> UITableViewCell? // Default: nil
        func collectionSkeletonView(_ skeletonView: UITableView, prepareCellForSkeleton cell: UITableViewCell, at indexPath: IndexPath)
    }
  • 구현
    self.tableView.dataSource = self
    
    ...
    
    extension ViewController: SkeletonTableViewDataSource {
      // tableView
      func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        self.dataSource.count
      }
      
      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyCell
        let data = dataSource[indexPath.row]
        cell.prepare(picture: data.image, title: data.title, description: data.description)
        return cell
      }
      
      // skeletonView
      func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
        return "cell"
      }
      
      func collectionSkeletonView(_ skeletonView: UITableView, skeletonCellForRowAt indexPath: IndexPath) -> UITableViewCell? {
        skeletonView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
      }
      
      func collectionSkeletonView(_ skeletonView: UITableView, numberOfRowsInSection section: Int) -> Int {
        UITableView.automaticNumberOfSkeletonRows // <- 편리하게 사용 가능
      }
      
      func numSections(in collectionSkeletonView: UITableView) -> Int {
        1
      }
    }​
  • 주의해야할 사항
    • 주의 - Cell 자체에도 isSkeletonenable = true 설정 필요
      import UIKit
      import SnapKit
      import SkeletonView
      
      class MyCell: UITableViewCell {
      
        // 각 UI에 $0.isSkeletonable = true 필요 (titleLabel.isSkeletonable = true)
        
        // 자체 cell에다가도 isSkeletonable = true 필요
        
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
          super.init(style: style, reuseIdentifier: reuseIdentifier)
          self.isSkeletonable = true // <- 
          self.contentView.isSkeletonable = true // <-
          
          ...
        }
      }​
  • 사용하는 쪽
    override func viewDidLoad() {
      super.viewDidLoad()
      ...
      
      let skeletonAnimation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: .leftRight)
        self.view.showAnimatedGradientSkeleton(usingGradient: .init(colors: [.lightGray, .gray]), animation: skeletonAnimation, transition: .none)
        self.fetchDataSource()
      }
      
      private func fetchDataSource() {
      DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
        let datas = (0...30)
          .map {
            MyData(
              image: UIImage(named: "jake"),
              title: "title \($0)",
              description: "description \($0)"
            )
          }
        self.dataSource.append(contentsOf: datas)
        self.tableView.reloadData()
        self.view.hideSkeleton(reloadDataAfter: true, transition: .crossDissolve(0.5))
      }
    }​

 

 

cf) UICollectionView에서 skeleton 적용 방법 

- SkeletonCollectionViewDataSource 준수

public protocol SkeletonCollectionViewDataSource: UICollectionViewDataSource {
    func numSections(in collectionSkeletonView: UICollectionView) -> Int  // default: 1
    func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier
    func collectionSkeletonView(_ skeletonView: UICollectionView, supplementaryViewIdentifierOfKind: String, at indexPath: IndexPath) -> ReusableCellIdentifier? // default: nil
    func collectionSkeletonView(_ skeletonView: UICollectionView, skeletonCellForItemAt indexPath: IndexPath) -> UICollectionViewCell?  // default: nil
    func collectionSkeletonView(_ skeletonView: UICollectionView, prepareCellForSkeleton cell: UICollectionViewCell, at indexPath: IndexPath)
}

* 전체 코드: https://github.com/JK0369/ExSkeletonView

 

* 참고

https://swiftobc.com/repo/Juanpe-SkeletonView-swift-ui

https://github.com/Juanpe/SkeletonView

Comments