Swift Xib 활용+Rx [2]

마이노·2023년 5월 2일
0
post-thumbnail

[1]에서는 BehaviorRelay로 accept를 했습니다. 이제는 뷰에서 해당 상태이상을 감지하는 Rx를 만들어보겠습니다.

override func viewDidLoad {
	Task { try await self.getFish() }
    // myData.accept(data) 
}

함수를 만들었으니 데이터를 한번 쏴주어야 겠죠. 로드되는 시점에서 데이터를 날려줍니다.

이제 mydata로 날아간 값을 어떻게 요리할것인지 필요합니다.

myData.bind(to: fishView.tableView.rx.items(cellIdentifier: Item.reuseIdentifier, cellType: Item.self)) {row, item, cell in 
// [1]과 [2]중 아무거나 사용해도 무관
// url String -> UIImage [1]
let _ = self.loadImage(item.image_url)
			.observe(on: MainScheduler.instance)
			.bind(to: cell.fishImageView.rx.image)
    // url String -> UIImage [2]
	cell.fishImageView.setImageUrl(item.image_url)
    
	cell.numberLabel.text = "\(item.number)"
	cell.fishLocationLabel.text = item.location
	cell.fishNameLabel.text = item.name
	cell.fishPriceLabel.text = "\(item.sell_nook)"
}.disposed(by: disposeBag)

myData에 있는 값을 bind시킵니다. bind는 묶는다는 말이니까 어디에 연결할것인지 물어보는 것과 같습니다. 저희는 fishView라는 UIView에 테이블뷰가 아울렛으로 존재하죠. 따라서 이 테이블뷰의 items(cell)에 바인드해야합니다.

items를 직접 살펴봅시다.

cellIdentifier에는 String이 들어가네요. 어떻게 활용하려는 걸까요?

어디서 많이 본 모양새입니다. 바로 Rx를 사용하지 않고 테이블 뷰를 생성하려고 할 때 코드와 동일합니다.

cellIdentifier: Item.reuseIdentifier, cellType: Item.self))

따라서 생김새를 확인했으니, [1]에서 만든 Extension을 통해 만들어둔 값을 넣어주시면 되겠습니다.

다음은 뒷 부분 클로저입니다.

세가지를 escape시키고 있습니다. 생김새를 보아하니 인덱스,요소,셀이 확실해집니다.

cell.numberLabel.text = "\(item.number)"

셀에는 이름을 띄울 수 있는 텍스트를 준비해보았습니다. 따라서 접근할 수 있는 label이 있겠고, label이 있으니 text를 set할 수 있겠습니다. 어떠한 요소를 받아서 넣을 것인지. 요소의 해당되는 값을 넣어주면 될 것 같습니다.

저는 여기에 헤더를 추가했었습니다.

extension FishViewController: UITableViewDelegate {
	func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
		let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: Header.reuseIdentifier) as! Header
		return v
    }
}

이렇게 코드를 짜고 실행해보면 이상하게도 헤더가 나오지 않습니다. 왜일까요?

fishView.tableView.rx.setDelegate(self).disposed(by: disposeBag)

대리자를 설정해주지 않아서 그렇습니다.
rx의 setDelegate는 일반 Delegate뿐만 아니라 Rx를 사용할 때 필요한 Delegate또한 사용가능하게끔 해줍니다.

final class FishViewHeader: UITableViewHeaderFooterView {
    enum Constant {
      static let size: CGSize = .init(width: UIView.noIntrinsicMetric, height: 35)
    }
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
	return Header.Constant.size.height
}

헤더도 마찬가지로 수정이 용이하게 구현부에 높이를 따로 만들어 쓰는 것이 좋습니다.

이미지 또한 캐시를 사용해 로딩을 최대한 줄이는 방향으로 만들어줍니다.

class ImageCacheManager {
    static let shared = NSCache<NSString, UIImage>()
    
    private init() {}
}

1. UIImageView +Extension

https://hryang.tistory.com/29

import UIKit

extension UIImageView {
    
    func setImageUrl(_ url: String) {
        
        let cacheKey = NSString(string: url)
        
        if let cachedImage = ImageCacheManager.shared.object(forKey: cacheKey) {
            print("yes")
            self.image = cachedImage
            return
        }
        
        DispatchQueue.global(qos: .background).async {
            if let imageUrl = URL(string: url) {
                URLSession.shared.dataTask(with: imageUrl) { (data, res, err) in
                    if let _ = err {
                        DispatchQueue.main.async {
                            self.image = UIImage()
                        }
                        return
                    }
                    DispatchQueue.main.async {
                        if let data = data, let image = UIImage(data: data) {
                            ImageCacheManager.shared.setObject(image, forKey: cacheKey)
                            self.image = image
                        }
                    }
                }.resume()
            }
        }
    }
}

//사용부
cell.citizenImage.setImageUrl(item.image_url)

2. Observable

    func loadImage(_ url: String) -> Observable<UIImage?> {
        
        let cache = NSString(string: url)
        
        return Observable.create { emitter in
            if let cachedImage = ImageCacheManager.shared.object(forKey: cache) {
                print("yes")
                emitter.onNext(cachedImage)
                emitter.onCompleted()
            }
            
            let myUrl = URL(string: url)!
            let task = URLSession.shared.dataTask(with: myUrl) { data, response, error in
                guard let data else {
                    emitter.onError(error!)
                    return
                }
                let image = UIImage(data: data)
                ImageCacheManager.shared.setObject(image!, forKey: cache)
                emitter.onNext(image)
                emitter.onCompleted()
            }
            task.resume()
            return Disposables.create {
                task.cancel()
            }
        }
    }
    
 //사용부
let _ = self.loadImage(item.image_url)
            .observe(on: MainScheduler.instance)
            .bind(to: cell.citizenImage.rx.image) 

profile
아요쓰 정벅하기🐥

0개의 댓글