iOS/Swift 코드로 UIView 만들기

Kim-leo·2024년 3월 4일
post-thumbnail

들어가며

안녕하세요. Kim-leo입니다.

이번에는 UIView 요소들(ex. UILabel, UIButton, UIImageView 등)을 코드로 생성하고 조작하는 방법을 적어보려 합니다.

사실, Main.storyboard 에서 직관적으로 View 안에 추가하고 UIButton 같은 경우에는 action 함수도 손쉽게 다룰 수 있는데요.

하지만, 화면 로딩 시 무거울 가능성이 있고, 앱이 커지고 복잡해질수록 가독성이 떨어집니다. 그리고 무엇보다 협업 시에도 많은 불편함이 있죠.

스토리보드에서의 작업이 장단점이 명확하지만, 이번에는 순수 code로 각 UIView 요소들을 선언하고, 디자인, 레이아웃, 동작 메소드 등의 방법을 간단히 알아보겠습니다.

한편, 위 요소를 ViewController 내부에 구현하지 않으려 합니다.

ViewController 안에 UIView 요소를 다 삽입할수도 있지만! View.swift 파일을 따로 생성하여 ViewController, 또는 데이터를 다루는 Model 등과 분리를 함으로써 MVC 패턴이나 MVVM 패턴에 바로 적용 가능하도록 하기 위함입니다.

View.swift 파일 생성

위 사진처럼 ViewController는 놔두고, View.swift 파일을 따로 생성했습니다.

UILabel 선언하기

이제 View.swift 파일 안에 직접 UIView를 만들어 봅니다.

// MARK: - View.swift

import Foundation
import UIKit

class MyView: UIView {
    lazy var titleLabel: UILabel = {
        let label = UILabel()
        // (이 자리에 label에 대한 설정 및 메소드 입력 가능)
        return label
    }()
    
}

MyView라는 이름의 UIView를 상속받은 클래스를 만들었습니다.
이 클래스 안에 UIView 중에서 가장 대표적이고 쉬운 UILabel을 만들었어요.

titleLabel 이름을 가진 UILabel의 설정을 좀 더 상세히 작성하겠습니다. 아래 코드와 같이요.

lazy var titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false     // 추후 레이아웃을 직접 설정해주기 위해 다음 메소드 값을 false로 설정
        label.text = "타이틀입니다."
        label.textAlignment = .center
        label.textColor = .darkGray
        label.backgroundColor = .systemGray6
        label.layer.masksToBounds = true
        label.layer.borderWidth = 1
        label.layer.borderColor = UIColor.black.cgColor
        return label
    }()

추가된 메소드는 사실 그 이름만 보아도 무슨 기능을 하겠구나 이해하시는데 큰 문제는 없을 것으로 예상됩니다. 정말 직관적이죠.

UILabel 레이아웃 설정

UILabel을 선언했으니, 이제 이를 View 내의 자리를 만들어보겠습니다.

UIView 자리를 만들어 주려면, 아래 두 메소드가 필요해요.

class MyView: UIView {
    lazy var titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false     // 레이아웃을 직접 설정해주기 위해 다음 메소드 값을 false로 설정
        label.text = "타이틀입니다."
        label.textAlignment = .center
        label.textColor = .darkGray
        label.backgroundColor = .systemGray6
        label.layer.masksToBounds = true
        label.layer.borderWidth = 1
        label.layer.borderColor = UIColor.black.cgColor
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init?(coder:) is not supported")
    }
    
}

override init(frame: CGRect) 메소드는 UIView 요소들이 실제로 화면 안에 추가되어, 어떻게 배치가 되는지 View를 초기화 하는 메소드입니다.

required init?(coder: NSCoder) 메소드는 필수 생성자로, 슈퍼 클래스(여기서는 UIView)를 상속받는 모든 서브 클래스(titleLabel 등)는 생성자의 재정의를 필수적으로 해주어야 합니다.

이제 override init(frame: CGRect) 메소드 안에 아래와 같이 titleLabel을 View에 추가하고 레이아웃을 설정합니다.

(참고로, 저 코드들은 titleLabel을 화면 정중앙에 놓고, 높이는 100으로 고정, 너비는 화면의 0.7배로 설정한 것입니다.)

UIView의 레이아웃을 이렇게 직접 설정할 수 있는데, 다른 다양한 방법이 있으니 직접 찾아보시는 것도 도움이 많이 될 것 같아요.

override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(titleLabel)
        
        titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
        titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
        titleLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
        titleLabel.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.7).isActive = true
        // 여기서 self는 MyView 클래스를 의미합니다.
    }

여기까지 코드를 작성하고 시뮬레이터를 돌리면!
아무것도 안 나옵니다... 당연하지요. View 파일안에 Label이니, MyView니 열심히 만들어 놓아도 ViewController 안에 선언을 하지 않으면 말짱 도루묵입니다.

ViewController 안에 View 불러오기

ViewController 으로 오면 아래와 같이 텅 비어 있죠.


// MARK: - ViewController.swift

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }


}

이제 여기에다 이전에 만들어 놓은 MyView 요소들을 직접 불러와 보겠습니다.

// MARK: - ViewController.swift

import UIKit

class ViewController: UIViewController {

    let myView = MyView()	// 짜잔.
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addSubview(myView) // ViewController 안에 보여지는 View에 myView 불러오기
    }


}

myView 라는 이름으로 MyView 클래스를 통째로 불러왔어요. 그러면 이 클래스 안에 있는 titleLabel도 쓸 수 있답니다. 그 다음, viewDidLoad 메소드 안에 myView를 추가했어요.

그리고 이젠 myView 자체의 레이아웃을 설정할 차례입니다. 아래와 같이요.
(setupUI 라는 이름의 함수를 따로 지정하여 myView에 대한 레이아웃 설정 기능을 하도록 만들었어요.)

class ViewController: UIViewController {

    let myView = MyView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addSubview(myView)
        setupUI()
    }

    func setupUI() {
        myView.translatesAutoresizingMaskIntoConstraints = false
        myView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
        myView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        myView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
        myView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
    }

}

(위의 setupUI 메소드를 요약하자면, myView의 윗부분, 양 옆부분, 아랫부분을 사용자에게 보여지는 실제 화면의 크기와 똑같이 딱 맞추었다고 생각하시면 됩니다.)

짜잔. 이제 잘 보인다.!

Mamury(마무리)

사실 UIView를 코드로 구현하는 방법이나 과정은 다른 훌륭하시고 친절하신 개발자 분들께서 많이 소개를 해주시고, 디테일한 디자인이나 설정 등은 검색하시면 정말 쉽게 찾을 수 있습니다.

이번 포스트의 취지는 UIView 요소들을 선언하고 다룰 때, ViewController 내부가 아닌 외부 파일에서 View를 다루는 법을 말씀드리고 싶었습니다.

정말 나~~중에 혹시 여러분들이 하실 프로젝트가 MVC 패턴이나 MVVM 패턴을 채택한다면 View를 위한 파일을 별도로 만들고 거기다가 View 관련 요소들을 다 작성해야 하는데요. 저도 옛날에는 ViewController 내부에 UIView 요소를 다 때려박곤 했는데 아키텍처 패턴을 고려하고 난 후부터는 View 파일에 따로 생성하려고 노력중입니당.

여하튼 읽어주셔서 감사합니다. :)

전체 소스 코드 깃허브 링크

profile
iOS Developer

0개의 댓글