swift / UIView / 같은 스타일의 요소가 반복될 때

임혜정·2024년 8월 7일
0

레이블을 상수,클로저로 선언하는 방식과 함수로 선언하는 방식

1. 상수+클로저 방식:

private let createLabel: UILabel = {
    let label = UILabel()
    label.text = text
    label.textAlignment = .center
    label.backgroundColor = .systemBlue
    label.textColor = .white
    return label
}

장:
1. 불변성: 실수로 변경될 가능성이 없다
2. 함수형 프로그래밍: 함수를 값으로 다루는 함수형 프로그래밍 스타일
3. 캡처: 외부 변수를 캡처하여 사용할 수 있어, 컨텍스트에 따라 다른 동작을 하는 레이블 생성기로 만들 수 있음

단:
1. 메모리 사용: 클로저가 생성될 때 메모리에 즉시 할당되며, 클래스의 인스턴스가 존재하는 동안 계속 메모리를 차지한다
2. 재사용성: 다른 클래스에서 재사용하기 위해서는 전체 클로저를 복사해야함

2. 함수 방식:

private func detailSmallLabel() -> UILabel {
        let label = UILabel()
        label.text = "small label"
        label.textAlignment = .center
        label.textColor = .white
        label.font = .boldSystemFont(ofSize: 18)
        return label
}
    
private func setupStackView() {
        [기타 요소, stackView].forEach {
            self.addSubview($0)
        }
        
        stackView.snp.makeConstraints {
            $0.top.equalTo(stackView1.snp.bottom).offset(10)
            $0.centerX.equalToSuperview()
            $0.width.equalTo(150)
            $0.height.equalTo(160)
        }

        addSmallLabel()
}

private func addSmallLabel() {
        (1...3).forEach { label in
            let label = detailSmallLabel()
            stackView.addArrangedSubview(label)
        }
}

장점:
1. 가독성: 전통적인 방식으로, 많은 개발자들에게 익숙하고 직관적
2. 메모리 효율성: 함수는 호출될 때만 스택에 올라가므로, 사용하지 않을 때는 메모리를 차지하지 않는다.
3. 재사용성: 상속을 통해 오버라이드하거나 extension을 통해 확장하기 쉽다.
4. 디버깅: 일반 함수이므로 디버깅이 상대적으로 쉽다.

단점:
1. 가변성: 메서드이므로 서브클래스에서 오버라이드될 수 있다 (의도치 않은 동작 변경 가능성)
2. 컨텍스트 캡처: 외부 변수를 캡처하여 사용하기 위해서는 추가적인 매개변수가 필요

정리 ) 뭘 쓸까?

  1. 불변성이 중요하고, 함수형 프로그래밍 스타일을 선호한다면 상수+클로저 방식이 좋다
  2. 메모리 효율성, 재사용성, 그리고 전통적인 객체지향 프로그래밍 스타일을 선호한다면 함수 방식이 좋다
  3. 해당 기능이 현재 클래스에서만 사용되고 변경될 가능성이 낮다면 상수+클로저 방식도 좋은 선택일 수 있다
  4. 여러 클래스에서 재사용되거나 오버라이드될 가능성이 있다면 함수 방식이 더 적합할 수 있다

일반적으로, 간단한 UI 컴포넌트 생성의 경우 함수 방식이 더 보편적으로 사용되며, 코드의 일관성과 유지보수성 측면에서 장점이 있지만 프로젝트의 특성과 팀의 코딩 스타일, 협의에 따라 선택이 달라질 수 있음!


같은 스타일요소가 반복되고 여백이 모두 일정하게 유지되야하는 경우

만약 이런 계산기버튼들과 비슷한 레이아웃을 설정해야할 시에는 함수방식을 써야함을 예감할 수 있고 훨씬 노가다와 코드량을 줄여준다.

간격과 사이즈같은 오토레이아웃 설정이 계속 반복되며 일괄적으로 수정해야할 요소는 상수로 빼내어 CGFloat으로 선언.

버튼 스타일도 계속 반복되므로 for문을 이용해 생성 노가다를 줄인다. 만약 버튼 수를 줄이거나 늘려야한다면 간단하게 숫자값만 변경하면 되는 것이다.

import UIKit
import SnapKit

class CalculatorStyleView: UIView {
    
    // MARK: - Layout Constants
    private enum Layout {
        static let buttonSize: CGFloat = 60
        static let buttonSpacing: CGFloat = 10
        static let edgeInsets: CGFloat = 20
    }
    
    // MARK: - UI Components
    private let mainStackView: UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.spacing = Layout.buttonSpacing
        stackView.distribution = .fillEqually
        return stackView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - 오토레이아웃, 버튼 생성
    private func setupUI() {
        backgroundColor = .white
        addSubview(mainStackView)
        
        mainStackView.snp.makeConstraints {
            $0.edges.equalToSuperview().inset(Layout.edgeInsets)
        }
        
        for row in 0..<4 {
            let rowStack = createRowStackView()
            mainStackView.addArrangedSubview(rowStack)
            
            for col in 0..<4 {
                let button = createButton(title: "\(row * 4 + col + 1)")
                rowStack.addArrangedSubview(button)
                
                button.snp.makeConstraints {
                    $0.size.equalTo(Layout.buttonSize)
                }
            }
        }
    }
    
    private func createRowStackView() -> UIStackView {
        let stackView = UIStackView()
        stackView.axis = .horizontal
        stackView.spacing = Layout.buttonSpacing
        stackView.distribution = .fillEqually
        return stackView
    }
    
    private func createButton(title: String) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle(title, for: .normal)
        button.titleLabel?.font = .boldSystemFont(ofSize: 20)
        button.backgroundColor = .systemBlue
        button.setTitleColor(.white, for: .normal)
        button.layer.cornerRadius = 8
        return button
    }
}


class testViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let calculatorView = CalculatorStyleView()
        view.addSubview(calculatorView)
        
        calculatorView.snp.makeConstraints {
            $0.center.equalToSuperview()
            $0.size.equalTo(CGSize(width: 300, height: 300))
        }
    }
}
profile
오늘 배운걸 까먹었을 미래의 나에게..⭐️

0개의 댓글