[swift] IGListDiffKit과 함께하는 줄바꿈 처리

IGListDiffKit은 iOS에서 데이터의 변경 사항을 효율적으로 처리하기 위한 라이브러리입니다. 이 라이브러리는 UICollectionView 및 UITableView와 함께 사용되어 데이터 소스의 변경을 자동으로 감지하고 UI를 업데이트합니다.

이번에는 IGListDiffKit을 사용하여 줄바꿈 처리를 할 때의 예제를 살펴보겠습니다. 줄바꿈 처리는 문자열의 길이에 따라 적절한 줄바꿈을 수행하는 것을 의미합니다.

1. IGListDiffable 프로토콜 준수

먼저, 줄바꿈 처리를 하려는 데이터 모델이 IGListDiffable 프로토콜을 준수해야 합니다. IGListDiffable 프로토콜은 데이터 모델을 비교하고 업데이트하는 데 필요한 메서드를 정의합니다.

class MyModel: NSObject, IGListDiffable{
    let text: String
    
    init(text: String) {
        self.text = text
    }
    
    // IGListDiffable 메서드 구현
    func diffIdentifier() -> NSObjectProtocol {
        return self
    }
    
    func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
        guard let object = object as? MyModel else {
            return false
        }
        
        return self.text == object.text
    }
}

위의 코드에서는 MyModel이 IGListDiffable 프로토콜을 준수하도록 정의되어 있습니다. text 속성은 문자열의 줄바꿈 처리를 위한 데이터입니다.

2. 화면에 표시되는 셀에 줄바꿈 처리

이제 줄바꿈 처리가 필요한 셀을 구현합니다. 예를 들어, UICollectionView의 셀을 구현한다고 가정해봅시다.

class MyCell: UICollectionViewCell {
    let label: UILabel
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // 레이블 초기화
        self.label = UILabel()
        self.label.numberOfLines = 0 // 여러 줄로 표시될 수 있도록 설정
        self.label.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(self.label)
        
        // 레이블 제약 조건 설정
        NSLayoutConstraint.activate([
            self.label.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
            self.label.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
            self.label.topAnchor.constraint(equalTo: self.contentView.topAnchor),
            self.label.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor)
        ])
    }
    
    func configure(with model: MyModel) {
        self.label.text = model.text
    }
}

위의 코드에서는 MyCell이 줄바꿈 처리를 위한 셀을 구현한 예입니다. UILabel의 numberOfLines 속성을 0으로 설정하여 여러 줄로 표시되도록 합니다.

3. IGListAdapterDelegate 구현

마지막으로, IGListAdapterDelegate를 구현하여 데이터 변경 시 셀을 업데이트하도록 합니다.

class ViewController: UIViewController, IGListAdapterDelegate {
    let adapter: ListAdapter
    let collectionView: UICollectionView
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        
        // 컬렉션 뷰 초기화
        let layout = UICollectionViewFlowLayout()
        self.collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        self.collectionView.backgroundColor = .white
        self.collectionView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(self.collectionView)
        
        // 컬렉션 뷰 제약 조건 설정
        NSLayoutConstraint.activate([
            self.collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            self.collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            self.collectionView.topAnchor.constraint(equalTo: self.view.topAnchor),
            self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
        ])
        
        // 어댑터 초기화
        self.adapter = ListAdapter(updater: ListAdapterUpdater(), viewController: self)
        self.adapter.collectionView = self.collectionView
        self.adapter.delegate = self
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 데이터 소스 초기화
        let items = [MyModel(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
                     MyModel(text: "Ut felis justo, blandit nec velit in, dapibus cursus nisl."),
                     MyModel(text: "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas."),
                     MyModel(text: "Curabitur ut dolor varius, rhoncus metus sed, sollicitudin nulla.")]
        self.adapter.dataSource = self
    }
    
    // 데이터 소스 구현
    extension ViewController: ListAdapterDataSource {
        func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
            return self.items
        }
        
        func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
            return MySectionController()
        }
        
        func emptyView(for listAdapter: ListAdapter) -> UIView? {
            return nil
        }
    }
    
    // 셀 컨트롤러 구현
    class MySectionController: ListSectionController {
        var model: MyModel?
        
        override func sizeForItem(at index: Int) -> CGSize {
            guard let model = self.model else {
                return .zero
            }
            
            let width = collectionContext?.containerSize.width ?? 0
            let height = model.text.height(withConstrainedWidth: width, font: UIFont.systemFont(ofSize: 14))
            
            return CGSize(width: width, height: height)
        }
        
        override func cellForItem(at index: Int) -> UICollectionViewCell {
            guard let cell = collectionContext?.dequeueReusableCell(of: MyCell.self, for: self, at: index) as? MyCell else {
                fatalError()
            }
            cell.configure(with: self.model!)
            return cell
        }
        
        override func didUpdate(to object: Any) {
            self.model = object as? MyModel
        }
    }
}

// 문자열의 높이 계산을 위한 확장 메서드
extension String {
    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
        
        return ceil(boundingBox.height)
    }
}

위의 코드에서는 MySectionController가 섹션 컨트롤러를 구현하고, sizeForItem 메서드를 통해 셀의 크기를 계산하고, cellForItem 메서드를 통해 셀에 데이터를 연결합니다. 또한, ViewController가 ListAdapterDataSource를 구현하여 데이터 소스를 관리합니다.

이제 IGListDiffKit과 함께 줄바꿈 처리를 적용한 UICollectionView를 사용할 수 있습니다. IGListDiffKit은 데이터 소스의 변경을 자동으로 감지하여 셀을 업데이트해줍니다.

참고 자료: