외부 이미지(url)를 포함하는 TableView 구현

2 답변 글타래를 보이고 있습니다
  • 글쓴이
    • 인담
      참가자
      • 글작성 : 9
      • 답글작성 : 9

      안녕하세요.

      이번 프로젝트에서 구현해야하는 내용 중 어려움이 생겨 질문을 드립니다.

       

      프로젝트에서 요구하는 사항은 다음과 같습니다.

      • 화면 진입시 외부 이미지 경로(url) 리스트가 포함된 데이터를 호출
      • 각 TableViewCell에 해당 외부 이미지 경로의 이미지를 그릴 수 있는 ImageView 구현

      여기서 제가 겪은 어려움은 외부 이미지의 크기(픽셀 사이즈)가 다르기 때문에 TableView의 Content 영역을 미리 계산할 수 없다는 게 문제입니다.

       

      현재까지 제가 구현한 방식은 다음과 같습니다.

      1. url로 받아온 데이터를 ImageView에 그린다.(kingfisher 사용)
      2. 받아온 데이터(image)의 크기를 통해 가로세로 비율을 구한다.

      3. 해당 가로세로 비율을 ImageView의  constraint로 autolayout을 구성한다.

       

      해당 코드로 구현할 시 나타나는 문제점은 cell이 재사용될 때 constraint가 충돌하는 경고 코드가 발생하고

      이미 한번 그려진 cell( 화면에 나타난 cell)은 이미지가 불러와도 cell을 다시 그리지 않는다.(이미지 높이가 0이라서 이미지가 나타나지 않는다.)

       

      해당 문제를 해결하려면 어떻게 해야할까요?

      아래에 코드 첨부하겠습니다.

       

      미리 감사드립니다.

       

      class ImageTableViewCell: UITableViewCell{

          

          var viewModel: TimeLineInfo.TimeLinesInfo.ItemInfo?{

              didSet{

                  setCellValue()

              }

          }

          var imageConstraint: NSLayoutConstraint?{

              didSet(oldVal){

                  if let old = oldVal{

                      mainImageView.removeConstraint(old)

                  }

                  if let imageConstraint = imageConstraint{

                      mainImageView.addConstraint(imageConstraint)

                  }

              }

          }

          

          let titleLabel: UILabel = {

              let aLabel = UILabel()

              aLabel.text = ” ”

              aLabel.numberOfLines = 0

              aLabel.textColor = .black

              aLabel.font = .systemFont(ofSize: CGFloat(17).sizeFitWidthDevice, weight: .medium)

              aLabel.translatesAutoresizingMaskIntoConstraints = false

              return aLabel

          }()

          let mainImageView: UIImageView = {

              let aImageView = UIImageView()

              aImageView.layer.cornerRadius = CGFloat(20).sizeFitWidthDevice

              aImageView.addBorder(borderWidth: 2, borderColor: ColorConstant.ContentColor.cLightGray)

              aImageView.clipsToBounds = true

              aImageView.contentMode = .scaleAspectFit

              aImageView.translatesAutoresizingMaskIntoConstraints = false

              return aImageView

          }()

          

          override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {

              super.init(style: style, reuseIdentifier: reuseIdentifier)

              

              contentView.backgroundColor = .white

              setupContentView()

          }

          

          required init?(coder: NSCoder) {

              fatalError(“init(coder:) has not been implemented”)

          }

          

          private func setupContentView(){

              contentView.addSubview(titleLabel)

              contentView.addSubview(mainImageView)

              

              setupLayout()

          }

          

          private func setupLayout(){

              let circleView = UIView()

              circleView.backgroundColor = .white

              circleView.layer.cornerRadius = CGFloat(6).sizeFitWidthDevice

              circleView.addBorder(borderWidth: 2, borderColor: ColorConstant.ContentColor.cMiddleGray)

              circleView.translatesAutoresizingMaskIntoConstraints = false

              circleView.widthAnchor.constraint(equalToConstant: CGFloat(12).sizeFitWidthDevice).isActive = true

              circleView.heightAnchor.constraint(equalToConstant: CGFloat(12).sizeFitWidthDevice).isActive = true

              

              let grayVerLine = UIView()

              grayVerLine.backgroundColor = ColorConstant.ContentColor.cLightGray

              grayVerLine.translatesAutoresizingMaskIntoConstraints = false

              grayVerLine.widthAnchor.constraint(equalToConstant: CGFloat(1).sizeFitWidthDevice).isActive = true

              

              contentView.addSubview(grayVerLine)

              contentView.addSubview(circleView)

              

              NSLayoutConstraint.activate([

                  

                  circleView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: CGFloat(24).sizeFitWidthDevice),

                  circleView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: CGFloat(24).sizeFitWidthDevice),

                  

                  grayVerLine.centerXAnchor.constraint(equalTo: circleView.centerXAnchor),

                  grayVerLine.topAnchor.constraint(equalTo: contentView.topAnchor),

                  grayVerLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),

                  

                  titleLabel.centerYAnchor.constraint(equalTo: circleView.centerYAnchor),

                  titleLabel.leadingAnchor.constraint(equalTo: circleView.trailingAnchor, constant: CGFloat(15).sizeFitWidthDevice),

                  titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: CGFloat(-24).sizeFitWidthDevice),

                  

                  mainImageView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: CGFloat(15).sizeFitWidthDevice),

                  mainImageView.leadingAnchor.constraint(equalTo: circleView.trailingAnchor, constant: CGFloat(15).sizeFitWidthDevice),

                  mainImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: CGFloat(-24).sizeFitWidthDevice),

                  mainImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: CGFloat(-24).sizeFitWidthDevice),

              ])

          }

          

          func setCellValue(){

              titleLabel.text = viewModel?.data.text

              imageConstraint?.isActive = false

              

              //이미지가 로드된 후 클로저 호출

              if let image = viewModel?.data.imageUrl{

                  mainImageView.setImageCacheWithConstraint(with: image) {[weak self] (result) in

                      

                      switch result{

                      case .success(let image):

                          self?.imageConstraint = self?.mainImageView.heightAnchor.constraint(equalTo: (self?.mainImageView.widthAnchor)!, multiplier: image.size.height/image.size.width)

                          

                          self?.imageConstraint?.isActive = true

                          self?.contentView.setNeedsLayout()

                          self?.contentView.layoutIfNeeded()

                          break

                      case .failure(_):

                          break

                      }

                  }

              }

          }

          

          override func prepareForReuse() {

              super.prepareForReuse()

              

              imageConstraint?.isActive = false

              imageConstraint = nil

              mainImageView.image = nil

          }

      }

    • 야곰
      키 마스터
      • 글작성 : 37
      • 답글작성 : 580

      이미지뷰의 제약을 변경한 후 contents view가 아니라 cell에게 setNeedsLayout과 layoutIfNeeded 메서드를 호출해 보실래요?

      만약 그래도 안된다면
      cellForRowAt indexPath 메서드 안에서 cell.setNeedsLayout()을 호출해보세요.

      이 글을 참고해보세요.

      더 자세히 알고 싶다면 WWDC 2018 – High Performance Auto Layout영상을 한 번 보면 좋을듯합니다.

      • 인담
        참가자
        • 글작성 : 9
        • 답글작성 : 9

        감사합니다 한번 시도해보겠습니다!!

        • 야곰
          키 마스터
          • 글작성 : 37
          • 답글작성 : 580

          넵, 해보시고 결과도 알려주세요~ㅎㅎ

    • 멍단비
      참가자
      • 글작성 : 10
      • 답글작성 : 98

      정확한 요구사항을 모르겠어서 도움이 될지는 모르겠지만 이미지 크기에 따라서 셀의 크기를 달리해주시는 거면
      제약은 그대로 두고 이미지 비율만 가져와서 델리게이트를 이용하는 방법도 있을 것 같아요.

      참고만 한 번 해보셔요~

2 답변 글타래를 보이고 있습니다
  • 답변은 로그인 후 가능합니다.

logo landscape small

사업자번호 : 743-81-02195
통신판매업 신고번호 : 제 2022-충북청주-1278 호
고객센터 : 카카오톡채널 @yagom