THIS IS ELLIE

iOS14 UISlider 레이아웃 안바뀌는 현상 본문

낑낑/Troubleshooting

iOS14 UISlider 레이아웃 안바뀌는 현상

Ellie Kim 2021. 10. 15. 15:28

일하다가 발견한 버그...?


일단 UISlider를 사용해야 했는데 어떠한 상황에 따라 UIControl의 isEnabled속성을 건드려야 했음.
(참고로 UIControl은 UIView를 상속한 사용자의 의도를 전달하는 시각적인 요소들의 기반이 되는 클래스이고 UIButton, UISlider 등이 UIControl클래스를 상속해서 사용되고 있음)

오늘 사용해볼 프로퍼티 isEnabled
isEnabled프로퍼티는 디폴트는 YES이고 만약 NO로 설정하면 터치 이벤트를 무시하고 서브클래스들은 다르게 그려질 거임.
일단 여기까지 오케이.

 


1. isEnabled

컨트롤이 활성화 상태인지를 나타내는 BOOL값이고 {get set}이라 직접적으로 수정 가능한 프로퍼티임.
이 값을 true로 설정하면 컨트롤 활성화할 수 있고 false면 비활성화 가능함.
false로 설정하면 터치 이벤트를 무시하고 자체적으로 다르게 그릴 수 있다고 함.
false로 설정하면 컨트롤의 state비트 마스크에 disabled플래그가 추가되고 true로 설정하면 해당 플래그가 제거된다고 함.
(참고로 state는 UIControl.State 컨트롤의 상태로, 비트 마스크 값으로 지정)

 

2. disabled 

disabled은 위에서 말한 비트 마스크에 추가되는 플래그임.
컨트롤이 비활성화된 것을 반영하기 위해 흐리게 표시되고 이는 isEnabled프로퍼티를 통해서 설정할 수 있음.


아래 이미지는 애플 자체적으로 비활성화된 것을 반영하기 위해 흐리게 표시되는 예시 이미지 

ㅇㅇ미세하게 레이아웃이 다르게 그려짐.
왼쪽은 isEnabled가 YES 즉 디폴트 상황
오른쪽은 isEnabled가 NO인 상황
당연히 프로퍼티를 변경해주면 레이아웃도 변경되어야 함.

+ 추가로 테스트
UIButton도 UIControl 상속해서 만든 클래스니 isEnabled프로퍼티가 존재하고
Enabled플래그 값에 따라 스토리보드에 즉각적으로 반영이 되고 있음.


그런데 이 기본적인 것이? 왜 안되었을까?
아래 코드는 테스트해보려고 대충 만든 코드. 
(완벽하게 상황이 일치하지는 않으나 비슷)

class ViewController: UIViewController {
    private let outerView: SliderView = {
        let view = SliderView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(outerView)
        
        outerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        outerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        outerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        outerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        outerView.heightAnchor.constraint(equalToConstant: 100).isActive = true
    }
}

outerView를 만들어주고 거기 안에 slider가 있는 구조.

class SliderView: UIView {
    private lazy var slider: UISlider = {
        let slider: UISlider = UISlider()
        slider.translatesAutoresizingMaskIntoConstraints = false
        slider.minimumValue = 0
        slider.maximumValue = 100
        slider.value = 0
        slider.addTarget(self, action: #selector(sliderValueChanged(sender:)), for: .valueChanged)
        return slider
    }()
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)

        settingUpLayout()
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // 상황에 따라 isEnabled을 다르게 준다.
        // slider.isEnabled = true
        // slider.isEnabled = false
    }
    
    private func settingUpLayout() {
        addSubview(slider)
        
        slider.leftAnchor.constraint(equalTo: leftAnchor, constant: 20).isActive = true
        slider.rightAnchor.constraint(equalTo: rightAnchor, constant: -20).isActive = true
        slider.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        slider.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    }
    
    @objc private func sliderValueChanged(sender: UISlider) {
        print(sender.value)
    }
}

그리고 layoutSubviews()에서 상황에 맞게 slider의 isEnabled를 변경해주는 구조.
상황에 맞게 분기 처리는 잘 되는데 레이아웃이 안 바뀐다?? 왜 안 되는 건지

 

아무리 돌려봐도 isEnabled에는 정상으로 값이 들어가 있음.
???????????????????? 왜 안되는데???????????????????
나랑 같은 거 겪은 사람 나와보세요. https://developer.apple.com/forums/thread/658526

몇 명 나옴ㅋㅋ

꼭 이렇게 해야 하나... 결국 내가 직접 다시 그려줌.

1. slider.setNeedsLayout()
2. slider.layoutIfNeeded()

애플 정답을 알려줘!
 

resource:
https://developer.apple.com/documentation/uikit/uicontrol/1618217-isenabledhttps://developer.apple.com/documentation/uikit/uicontrol/state/1618247-disabled

반응형