• 카테고리

    질문 & 답변
  • 세부 분야

    모바일 앱 개발

  • 해결 여부

    미해결

cornerRadius가 적용이 안되는 문제가 있어 질문드립니다.

23.03.22 22:44 작성 23.03.22 22:44 수정 조회수 576

1

- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요!
- 먼저 유사한 질문이 있었는지 검색해보세요.
- 서로 예의를 지키며 존중하는 문화를 만들어가요.
- 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.

안녕하세요. 앨런님

현재 코드로 ToDoApp을 만들고 있는데 DetailView 부분을 코딩하면서 색상 버튼 부분에 cornerRadius가 적용이 안되는 문제가 있어 질문드립니다.

현재까지 제가 해결을 위해 확인한 부분은 아래와 같습니다.

  1. setupSaveButtonConstraints()를 호출 시 red, green, blue, purple 버튼에 cornerRadius 적용되지 않습니다.

  2. setupButtonCorner() 함수의 호출 지점을 layoutSubView()의 super 호출 전 또는 후로 설정해 보았지만 적용되지 않았습니다.

  3. setupButtonCorner() 함수에서 button에 height를 설정해서 실행해 보았지만 적용되지 않았습니다.

이 문제를 해결할 수 있는 방법이 뭐가 있을까요?

import UIKit

final class DetailView: UIView {
    
    // MARK: - Color Buttons
    let redButton: UIButton = {
        let button  = UIButton()
        button.setTitle("Red", for: .normal)
//        button.setTitleColor(MyColor.red.buttonColor, for: .normal)
//        button.backgroundColor = MyColor.red.backgroudColor
        button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
//        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    let greenButton: UIButton = {
        let button = UIButton()
        button.setTitle("Green", for: .normal)
//        button.setTitleColor(MyColor.green.buttonColor, for: .normal)
//        button.backgroundColor = MyColor.green.backgroudColor
        button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
//        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    let blueButton: UIButton = {
        let button = UIButton()
        button.setTitle("Blue", for: .normal)
//        button.setTitleColor(MyColor.blue.buttonColor, for: .normal)
//        button.backgroundColor = MyColor.blue.backgroudColor
        button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
//        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    let purpleButton: UIButton = {
        let button = UIButton()
        button.setTitle("Purple", for: .normal)
//        button.setTitleColor(MyColor.purple.buttonColor, for: .normal)
//        button.backgroundColor = MyColor.purple.backgroudColor
        button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
//        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    lazy var buttons: [UIButton] = [redButton, greenButton, blueButton, purpleButton]
    
    lazy var colorButtonStackView: UIStackView = {
        let st = UIStackView(arrangedSubviews: [redButton, greenButton, blueButton, purpleButton])
        st.axis = .horizontal
        st.spacing = 15
        st.alignment = .fill
        st.distribution = .fillEqually
        
        return st
    }()
    
    // MARK: - Text View
    let mainTextView: UITextView = {
        let textView = UITextView()
        textView.backgroundColor = .clear
        textView.font = UIFont.systemFont(ofSize: 14)
        textView.textColor = .black
        
        return textView
    }()
    
    lazy var backgroundView: UIView = {
        let view = UIView()
        view.addSubview(mainTextView)
        view.backgroundColor = MyColor.red.backgroudColor
        
        return view
    }()
    
    // MARK: - Update Button
    let saveButton: UIButton = {
        let button = UIButton()
        button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = MyColor.red.backgroudColor
        
        return button
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupDetailView()
        setupButtonColor()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) {
          fatalError("init(coder:) has not been implemented")
    }
    
    func setupDetailView() {
        self.backgroundColor = .white
        self.addSubview(colorButtonStackView)
        self.addSubview(backgroundView)
        self.addSubview(saveButton)
        
        backgroundView.clipsToBounds = true
        backgroundView.layer.cornerRadius = 10
        
        saveButton.clipsToBounds = true
        saveButton.layer.cornerRadius = 8
        
        layoutSubviews()
    }
    
    func setupConstraints() {
        setupColorButtonSVConstraints()
        setupBackgroundViewConstraints()
        setupMainTextViewConstraints()
        
        // 왜 이걸 설정하면 색깔 버튼의 cornerRadius가 변경이 안될까요?
        setupSaveButtonConstraints()
//        setupButtonCorner()
    }
    
    func setupColorButtonSVConstraints() {
        print(#function)
        colorButtonStackView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            colorButtonStackView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 25),
            colorButtonStackView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -25),
            colorButtonStackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20),
//            colorButtonStackView.bottomAnchor.constraint(equalTo: backgroundView.topAnchor, constant: -40),
            colorButtonStackView.heightAnchor.constraint(equalToConstant: 35)
        ])
    }
    
    func setupBackgroundViewConstraints() {
        print(#function)
        backgroundView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            backgroundView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 25),
            backgroundView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -25),
            backgroundView.topAnchor.constraint(equalTo: colorButtonStackView.bottomAnchor, constant: 40),
//            backgroundView.bottomAnchor.constraint(equalTo: saveButton.topAnchor, constant: -40),
            backgroundView.heightAnchor.constraint(equalToConstant: 200)
        ])
    }
    
    func setupMainTextViewConstraints() {
        print(#function)
        mainTextView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            mainTextView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 15),
            mainTextView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: -15),
            mainTextView.topAnchor.constraint(equalTo: backgroundView.topAnchor, constant: 8),
            mainTextView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: -8)
        ])
    }
    
    func setupSaveButtonConstraints() {
        print(#function)
        saveButton.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            saveButton.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 25),
            saveButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -25),
            saveButton.topAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: 40),
            saveButton.heightAnchor.constraint(equalToConstant: 40)
        ])
    }
    
    func setupButtonColor() {
        print(#function)
        buttons.forEach { button in
            button.setTitleColor(MyColor(rawValue: Int64(buttons.firstIndex(of: button)!) + 1)?.buttonColor, for: .normal)
            button.backgroundColor = MyColor(rawValue: Int64(buttons.firstIndex(of: button)!) + 1)?.backgroudColor
        }
    }

    override func layoutSubviews() {
        print(#function)
        super.layoutSubviews()
        setupButtonCorner()
    }
    
    func setupButtonCorner() {
        print(#function)
        buttons.forEach { button in
            button.clipsToBounds = true
            button.layer.cornerRadius = button.bounds.height / 2
        }
        
    }
}

답변 1

답변을 작성해보세요.

0

네 안녕하세요 석현 님.

지금 코드로 잡으실 때, 약간 문제가 있을 수 있는데..
이것을 해결하기 위해서

(1) 버튼들이 스택뷰 내부에 들어있기 때문에, 스택뷰 높이를 기준으로 layoutSubviews를 하시거나

button.layer.cornerRadius = colorButtonStackView.bounds.height / 2

 

(2) 아니면, (뷰 내부에서) 스택뷰 내부의 프레임들을 먼저 확정하기 위해

override func layoutSubviews() {
     super.layoutSubviews()
     colorButtonStackView.layoutIfNeeded()    // 스택뷰를 기준으로 먼저 레이아웃 확정⭐️
     setupButtonCorner()
}


이렇게 위의 2가지 방법 중 한가지를 선택하시면 문제가 해결되실꺼예요.

왜그런지 이유에 대해서 조금만 설명을 드리면.. 강의에서 (더 자세하게) 뷰 들간의 순서는 말씀 안 드린 것 같아 조금 더 자세히 말씀드리면 updateContraints 와 layoutSubviews도 사실 내부적으로 상위/하위 뷰들간의 순서가 있습니다.

즉, 뷰가 여러개 겹처 있을때 (각자 따로 addSubview 되어 있을때)

  • 하위 (updateConstraints) ➡️ 중간 (updateConstraints) ➡️ 상위 (updateConstraints) ➡️ 상위 (layoutSubviews) ➡️ 중간 (layoutSubviews) ➡️ 하위 (layoutSubviews)

이런 식으로 각 뷰들간의 연쇄적 호출 순서가 있습니다.

오토레이아웃은 하위 뷰 ➡️ 상위 뷰 순서로 (그리고 이제 오토레이아웃으로 frame이 다 결정된 후 일테니) 이제부터는 뷰들의 배치를 상위 뷰 ➡️ 하위 뷰 순서로 하게 됩니다.

즉, updateConstraints는 하위 ➡️ 상위
layoutSubviews는 상위 ➡️ 하위 로 순서가 일어난다는 것이죠.

근데 여기서는 버튼이 스택뷰에 들어있고, 스택뷰가 뷰에 다시 들어있으니.. 뷰들간의 계층이 생깁니다. 그런데 가장 상위의 뷰의 layoutSubviews시점에 하위 버튼의 프레임을 알수가 없기 때문에 이런 문제가 생기는 것입니다. 그래서 제가 위에 말씀드린 2가지 중에 한가지 방법으로 문제를 해결할 수 있습니다.

뷰의 layoutSubviews시점에 스택뷰는 프레임이 결정되니
첫번째 방법 - 스택뷰의 높이를 기준으로 cornerRadius를 준다. 또는
두번째 방법 - 스택뷰의 layoutIfNeeded( ) 를 호출해서 하위뷰들의 frame을 먼저 결정시키고 난 뒤 cornerRadius를 준다는 것입니다.


코드를 적용시켜보세요! 감사합니다. :)

정석현님의 프로필

정석현

질문자

2023.03.23

감사합니다. 답변을 통해서 문제를 해결했습니다.