레이블을 상수,클로저로 선언하는 방식과 함수로 선언하는 방식
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. 재사용성: 다른 클래스에서 재사용하기 위해서는 전체 클로저를 복사해야함
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. 컨텍스트 캡처: 외부 변수를 캡처하여 사용하기 위해서는 추가적인 매개변수가 필요
일반적으로, 간단한 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))
}
}
}