Swift로 UIScrollView와 UIStackView를 활용해 UITableView 및 UICollectionView 기능 구현하기 (뷰 태그와 가로 스크롤 포함)

작성일 :

Swift로 UIScrollView와 UIStackView를 활용해 UITableView 및 UICollectionView 기능 구현하기 (뷰 태그와 가로 스크롤 포함)

iOS 개발에서는 UITableView와 UICollectionView가 자주 사용되지만, 특정 상황에서는 UIScrollView와 UIStackView를 조합하여 이들 기능을 직접 구현해야 할 때가 있습니다. 이 글에서는 Swift를 사용해 UIScrollView와 UIStackView를 이용하여 UITableView와 UICollectionView와 유사한 기능을 어떻게 구현할 수 있는지 살펴보겠습니다.

1. 프로젝트 설정

먼저, Xcode에서 새로운 프로젝트를 생성하고, Single View App 템플릿을 선택합니다. 프로젝트가 생성되면, Main.storyboard를 열고 ViewController에 UIScrollView와 UIStackView를 추가합니다.

2. UIScrollView와 UIStackView 설정

UIScrollView는 콘텐츠가 화면을 벗어날 때 스크롤할 수 있도록 해주는 컨테이너 뷰입니다. UIStackView는 여러 뷰를 수직 또는 수평으로 정렬할 수 있도록 도와줍니다. 이번 예제에서는 UIStackView를 UIScrollView의 자식으로 추가하여, 스크롤 가능한 콘텐츠를 만듭니다.

swift
import UIKit

class ViewController: UIViewController {

    let scrollView = UIScrollView()
    let stackView = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()

        // ScrollView 설정
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])

        // StackView 설정
        stackView.axis = .vertical
        stackView.distribution = .fill
        stackView.alignment = .fill
        stackView.spacing = 10
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])

        // 샘플 데이터 추가
        for i in 1...20 {
            let label = UILabel()
            label.text = "Item \(i)"
            label.textAlignment = .center
            label.backgroundColor = .lightGray
            label.heightAnchor.constraint(equalToConstant: 50).isActive = true
            stackView.addArrangedSubview(label)
        }
    }
}

3. UITableView와 유사한 기능 구현

UITableView의 대표적인 기능 중 하나는 셀을 재사용할 수 있다는 점입니다. 이를 구현하기 위해 재사용 가능한 커스텀 뷰를 만들어야 합니다.

swift
import UIKit

class CustomView: UIView {

    let label = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupView() {
        label.translatesAutoresizingMaskIntoConstraints = false
        addSubview(label)
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
            label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
            label.topAnchor.constraint(equalTo: topAnchor),
            label.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
        label.textAlignment = .center
        backgroundColor = .lightGray
    }

    func configure(text: String) {
        label.text = text
    }
}

이제 CustomView를 사용하여 UIStackView에 추가해 보겠습니다.

swift
import UIKit

class ViewController: UIViewController {

    let scrollView = UIScrollView()
    let stackView = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupScrollView()
        setupStackView()
        addSampleData()
    }

    private func setupScrollView() {
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }

    private func setupStackView() {
        stackView.axis = .vertical
        stackView.distribution = .fill
        stackView.alignment = .fill
        stackView.spacing = 10
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])
    }

    private func addSampleData() {
        for i in 1...20 {
            let customView = CustomView()
            customView.configure(text: "Item \(i)")
            customView.heightAnchor.constraint(equalToConstant: 50).isActive = true
            stackView.addArrangedSubview(customView)
        }
    }
}

4. UICollectionView와 유사한 기능 구현

UICollectionView는 수평 및 수직 스크롤이 가능합니다. 이를 구현하기 위해 UIScrollView의 contentSize를 조정하고, 가로 방향으로 UIStackView를 구성합니다.

swift
import UIKit

class HorizontalViewController: UIViewController {

    let scrollView = UIScrollView()
    let stackView = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupScrollView()
        setupStackView()
        addSampleData()
    }

    private func setupScrollView() {
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        scrollView.showsHorizontalScrollIndicator = true
        scrollView.contentSize = CGSize(width: view.frame.width * 2, height: view.frame.height)
    }

    private func setupStackView() {
        stackView.axis = .horizontal
        stackView.distribution = .fill
        stackView.alignment = .fill
        stackView.spacing = 10
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
        ])
    }

    private func addSampleData() {
        for i in 1...10 {
            let customView = CustomView()
            customView.configure(text: "Item \(i)")
            customView.widthAnchor.constraint(equalToConstant: 150).isActive = true
            stackView.addArrangedSubview(customView)
        }
    }
}

5. 태그를 사용한 뷰 관리

뷰 태그를 사용하여 특정 뷰를 관리할 수 있습니다. 예를 들어, 특정 아이템을 선택할 때 태그를 이용해 쉽게 접근할 수 있습니다.

swift
class ViewController: UIViewController {

    let scrollView = UIScrollView()
    let stackView = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupScrollView()
        setupStackView()
        addSampleData()
    }

    private func setupScrollView() {
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }

    private func setupStackView() {
        stackView.axis = .vertical
        stackView.distribution = .fill
        stackView.alignment = .fill
        stackView.spacing = 10
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])
    }

    private func addSampleData() {
        for i in 1...20 {
            let customView = CustomView()
            customView.configure(text: "Item \(i)")
            custom

View.heightAnchor.constraint(equalToConstant: 50).isActive = true
            customView.tag = i
            stackView.addArrangedSubview(customView)
        }
    }

    func getViewByTag(tag: Int) -> CustomView? {
        return stackView.arrangedSubviews.first { $0.tag == tag } as? CustomView
    }
}

이 예제에서는 getViewByTag 메소드를 사용하여 특정 태그를 가진 뷰를 쉽게 가져올 수 있습니다.

결론

UIScrollView와 UIStackView를 사용하여 UITableView와 UICollectionView와 같은 기능을 구현하는 방법을 살펴보았습니다. 이 접근 방식은 재사용 가능한 커스텀 뷰와 뷰 태그를 활용하여 효율적인 스크롤과 뷰 관리를 가능하게 합니다. 이 방법을 통해 유연하게 사용자 인터페이스를 구성하고, 다양한 스크롤 뷰를 구현할 수 있습니다.