Roen의 iOS 개발로그

padding을 가지는 UITextField 만들기(feat. layoutMargins)

by Steady On

iOS

서론

UITextField 내부의 text의 주변으로 적절한 padding이 들어가도록 하는 커스텀 클래스를 만들어보려고 합니다.

목표

 

이런 식으로 박스안에 text가 적절하게 들어있으면서 또 UITextField의 특성상 내부 텍스트의 길이에 맞춰 자동으로 늘어났다가 줄어들었다가 해야겠죠? 여러가지 방법이 있겠지만, 저는 좀 쉬운 방법으로 UIView 내부에 UILabel을 넣어주고, margins를 잡아주는 방법으로 구현해 보았습니다.

 

구현

1. 먼저 UIView를 상속받는 커스텀 클래스를 하나 만들어줍니다. 그리고 당연히 UILabel이 들어갈거니까 그것까지 한번 만들어볼까요?

class MessageBubbleView: UIView {
	private let label = UILabel()
}

일단 저는 외부에서 UILabel을 만들어서 할당하는 것을 원하지는 않기 때문에 private로 설정해주었습니다. label에 관련된 모든 설정은 다 클래스 내부에서 제공하는 프로퍼티로 이루어지도록 할거에요.

 

2. 생성자를 통해 text를 받아와서 label에 할당하도록 만들어봅니다.

convenience init(text: String) {
    self.init()

    self.label.text = text
}

UIView의 init을 override 하는 방법도 있겠지만, 지정생성자를 통해서 frame을 변경한다거나 하지는 않을거라서 편의생성자로도 충분할 것 같아요!

 

3. label을 UIView의 subView로 추가하고, UIView 내부에서의 위치를 잡아보겠습니다. 

private func setUpSubView() {
    self.addSubview(self.label)
    self.label.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
        self.label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
        self.label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
        self.label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
        self.label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
    ])
}

이렇게 메서드를 만들어서 init에서 호출해주겠습니다.

NSLayoutConstraint 코드를 잘 보시면 label의 top, leading, trailing, bottom Anchor 모두를

layoutMarginsGuide와 똑같이 맞춰주고 있는 것을 볼 수 있는데요. 이 layoutMarginsGuide에 대해서는 이전의 포스팅을 참고해주세요!

 

2023.08.27 - [iOS] - layoutMarginsGuide 파헤치기

 

layoutMarginsGuide 파헤치기

서론 SuperView와 SubView 사이에 안쪽 padding을 주고 싶을 때 사용하는 layoutMarginsGuide에 대해 알아보겠습니다. layoutMarginsGuide https://developer.apple.com/documentation/uikit/uiview/1622651-layoutmarginsguide layoutMarginsGui

steady-on.tistory.com

 

지금까지의 전체 코드를 볼게요.

class MessageBubbleView: UIView {
    
    private var label = UILabel()
    
    convenience init(text: String) {
        self.init()
    
        self.label.text = text
        
        setUpSubView()
    }
    
    private func setUpSubView() {
    	self.label.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(self.label)
        
        NSLayoutConstraint.activate([
            self.label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
            self.label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
            self.label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
            self.label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
        ])
    }
}

이렇게만 해놔도 사실 사용하는 쪽에서 backgroundColor라던가 호출해서 사용할 수 있지만, label을 private로 설정한 탓에 label에 관련된 건 아무것도 설정할 수가 없을거에요. 그리고 목표하는 사진처럼 흰색 배경에 둥근 모서리를 매번 설정한다면 그것도 번거로울 거고요. label에 대해 설정할 만한 속성은 text, font, numberOfLines 정도가 있을 거고..(textColor는 기본값을 사용하겠다는 설정이라 생략!) 말풍선이될 view에 대한 속성은 서브뷰와 슈퍼뷰 사이에 얼마나 margin값을 줄것인지와 layer.cornerRadius 정도만 좀 따로 만들어볼게요.

 

var text: String? {
    get { label.text }
    set { label.text = newValue }
}

var font: UIFont! {
    get { label.font }
    set { label.font = newValue }
}

var numberOfLines: Int {
    get { label.numberOfLines }
    set { label.numberOfLines = newValue }
}

var margins: NSDirectionalEdgeInsets {
    get { directionalLayoutMargins }
    set { directionalLayoutMargins = newValue }
}

var cornerRadius: CGFloat {
    get { layer.cornerRadius }
    set { layer.cornerRadius = newValue }
}
 

연산 프로퍼티를 활용해서 이렇게 label에 대한 속성을 설정할 수 있게 해주고, margins 같은 경우는 layoutMargins보다 directionalLayoutMargins를 사용할 수 있도록 이름을 좀 축약해서 설정해봤어요. 또 cornerRadius도 layer를 한번 거쳐서 들어가야 하니까 프로퍼티로 만들어봤습니다.

 

그럼 생성자에서도 self.label.text 대신 만들어둔 프로퍼티를 쓰면 편하겠죠? 또 default 디자인 값을 메서드로 만들어서 초기화될때 불러주면 편할 거 같네요! 전체 내용을 반영한 코드는 다음과 같습니다!

 

import UIKit

class MessageBubbleView: UIView {
    
    private var label = UILabel()
    
    var text: String? {
        get { label.text }
        set { label.text = newValue }
    }
    
    var font: UIFont! {
        get { label.font }
        set { label.font = newValue }
    }
    
    var numberOfLines: Int {
        get { label.numberOfLines }
        set { label.numberOfLines = newValue }
    }
    
    var margins: NSDirectionalEdgeInsets {
        get { directionalLayoutMargins }
        set { directionalLayoutMargins = newValue }
    }
    
    var cornerRadius: CGFloat {
        get { layer.cornerRadius }
        set { layer.cornerRadius = newValue }
    }
    
    convenience init(text: String) {
        self.init()
    
        self.text = text
        
        setDefaultDesign()
        setUpSubView()
    }
    
    private func setDefaultDesign() {
        backgroundColor = .white
        self.cornerRadius = 10
        self.font = UIFont(customFont: .cafe24SupermagicRegular, size: 17)
        self.numberOfLines = 0
    }
    
    private func setUpSubView() {
        self.addSubview(self.label)
        self.label.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            self.label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
            self.label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
            self.label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
            self.label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
        ])
    }
}

 

마무리

스토리보드로 UILabel에 패딩을 줘야할 때는 높이를 내용물에 따라 유동적으로 조절하기가 쉽지 않아서 버튼으로 구현을 한다거나 사실 스스로도 이해하지 못한 구글링으로 발견한 코드를 복붙해서 사용하곤 했는데요. 코드베이스로 UI를 짜게되면서 이런것도 간단하게 구현할 수 있게되어서 공부해나가는 보람이 있는것 같습니다!

 

블로그의 정보

Roen의 iOS 개발로그

Steady On

활동하기