본문 바로가기
iOS/UIKit

[iOS] Floating Button의 모든것 + StackView

by BrickSky 2023. 6. 29.

만들고 싶은 뷰


해당 뷰를 만들기 위해서는 StackView와 Floating Button에 관한
이해가 필요하다.
 
이를 활용해서 해당 뷰를 만들어보자!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
🥹 then을 사용하지 않기로 해서 살짝 어색하지만 해보기로 했다.
 
 

class FloatingViewController: UIViewController {
    
    // 배경
    lazy var backgroundView: UIView = {
        let view = UIView(frame: self.view.frame)
        view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.8)
        view.isHidden = true
        self.view.insertSubview(view, belowSubview: self.plusButton)
        return view
    }()
  
    // StackView를 통해 화면 UIButton 넣을 예정
    lazy var stackView: UIStackView = {
        let view = UIStackView()
        view.axis = .vertical
        view.spacing = 25
        view.alignment = .fill
        view.distribution = .fillEqually
        view.isHidden = true
        return view
    }()
    
    // StackView에 들어갈 Button
    lazy var plusButton: UIButton = {
        let button = UIButton()
        button.setImage(UIImage(named: "Plus"), for: .normal)
//        button.addTarget(self, action: #selector(<#T##@objc method#>), for: .touchUpInside)
        return button
    }()
    
    // StackView에 들어갈 Button
    lazy var wineButton: UIButton = {
        let button = UIButton()
        button.setImage(UIImage(named: "Wine"), for: .normal)
//        button.addTarget(self, action: #selector(<#T##@objc method#>), for: .touchUpInside)
        return button
    }()
    
    // StackView에 들어갈 Button
    lazy var coffeeButton: UIButton = {
        let button = UIButton()
        button.setImage(UIImage(named: "Coffee"), for: .normal)
//        button.addTarget(self, action: #selector(<#T##@objc method#>), for: .touchUpInside)
        return button
    }()
    
    // StackView에 들어갈 Button
    lazy var cokeButton: UIButton = {
        let button = UIButton()
        button.setImage(UIImage(named: "Coke"), for: .normal)
//        button.addTarget(self, action: #selector(<#T##@objc method#>), for: .touchUpInside)
        return button
    }()

우선 addTarget에 들어갈 함수는 아직 구현하지 않았기에 주석 처리를 했고
나머지 UI에 관한 부분만 우선적으로 코드를 작성했다.
 

StackView를 활용해라!


StackView는 처음 써봤기에 공부가 필요했다.
 

func setLayout() {
        view.addSubviews(backgroundView, stackView, plusButton)
        
        stackView.addSubview(wineButton)
        stackView.addSubview(wineButton)
        stackView.addSubview(cokeButton)
        
        backgroundView.snp.makeConstraints {
            $0.edges.equalToSuperview()
        }
        
        plusButton.snp.makeConstraints {
            $0.trailing.equalToSuperview().inset(50)
            $0.bottom.equalToSuperview().inset(70)
            $0.width.height.equalTo(50)
        }
        stackView.snp.makeConstraints {
            $0.trailing.equalToSuperview().inset(50)
            $0.bottom.equalTo(plusButton.snp.top).inset(20)
            $0.width.equalTo(50)
            $0.height.equalTo(200)
        }
    }

 
wineButton, wineButton, cokeButton을 stackView에 올려주고자 각각 코드를 작성했다.
또한 StackView는 각각의 Button 그리고 spacing를 고려해서 200이라는 height를 지정해주었다.
 

이제는 plusButton을 클릭하면, StackView의 이미지들이 보여야 한다.
 
 

클릭을 해도 보이지 이미지 에셋들이 StackView에 보이지 않는다.. (왜 안보이지??)
 
 

plus 버튼을 눌렀더니 버튼이 올라오긴 한다!! 

 

addSubView와 addArrangedSubView의 차이점은?


addSubview는 UIView의 인스턴스 메소드이다.
반면 addArrangedSubview는 UIStackview의 인스턴스 메소드이다.
즉, addSubview는 뷰의 위치와 크기를 사람인 “내가”직접 관리해야하지만,
addArrangedSubview는 스택 뷰가 레아이웃을 자동으로 관리하기 때문에
 

view.axis = .vertical
        view.spacing = 25
        view.alignment = .fill
        view.distribution = .fillEqually
        view.isHidden = true

이런 식으로 코드를 깔끔간결하게 작성해주면 된다ㅋㅎ

 

불리언 값을 활용해보자 


@objc
    func floatingButtonTapped(){
        stackView.isHidden = !stackView.isHidden
        print("plus버튼 클릭!!!")
    }

! 는 Swift에서 !는 부정 연산자로, 불리언 값을 반전시킨다. 즉, true는 false로, false는 true로 바꾸는 건데,
 
 

lazy var clickedbackgroundView: UIView = {
        let view = UIView(frame: self.view.frame)
        view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.8)
        view.isHidden = true
        self.view.insertSubview(view, belowSubview: self.floatingButton)
        return view
    }()

view.isHidden = true 을 기본으로 설정했으니까, ! 를 활용해서 true면 false로 false면 true로 바꾸는 것이다.
 

이렇게 클릭을 하면 스택뷰의 이미지들이 보여졌다, 사라졌다! 하는 것을 확인할 수 있다.
이제 배경을 흐리게 하는 코드를 이어서 작성해보려 한다.

 

불리언 값을 응용해보자


위에서 말한 것 처럼, 배경을 흐리게 하려고 한다.
마찬가지로 불리언 값을 활용해 버튼을 클릭했을 때 clickedBackgroundView를 보여줄 예정이다.
기존에는 hidden 처리를 해두고, 버튼을 누르면 보여지는 코드를 작성했다.
stackView때 사용했던 로직과 같게!

@objc
    func floatingButtonTapped(){
        stackView.isHidden = !stackView.isHidden
        clickedbackgroundView.isHidden = !clickedbackgroundView.isHidden
        print("plus버튼 클릭!!!")
    }

 

해냈다!

 

애니메이션을 넣어보자


#1 배경을 흐려지게 하는 첫번째 애니메이션!

UIView의 animate를 활용해 배경이 바뀌는 속도가 바뀌는 것을 알 수 있다.

@objc
    func floatingButtonTapped(){
        stackView.isHidden = !stackView.isHidden
        clickedbackgroundView.isHidden = !clickedbackgroundView.isHidden

        print("plus버튼 클릭!!!")

        UIView.animate(withDuration: 0.5) {
            if self.clickedbackgroundView.isHidden {
                self.clickedbackgroundView.alpha = 0
            } else {
                self.clickedbackgroundView.alpha = 1
            }
        }
    }

 

#2 플로팅 버튼이 움직이는 두번째 애니메이션


@objc
    func floatingButtonTapped(){
        stackView.isHidden = !stackView.isHidden
        clickedbackgroundView.isHidden = !clickedbackgroundView.isHidden
        
        print("plus버튼 클릭!!!")
        
        UIView.animate(withDuration: 0.5) {
            if self.clickedbackgroundView.isHidden {
                self.clickedbackgroundView.alpha = 0
            } else {
                self.clickedbackgroundView.alpha = 1
            }
        }
        
        let rotationAngle = CGFloat.pi / 4
        UIView.animate(withDuration: 0.3) {
            self.floatingButton.transform = CGAffineTransform(rotationAngle: rotationAngle)
        }
    }

let rotationAngle = CGFloat.pi / 4
이 코드는 1라디안인 180도를 의미한다. 즉 180을 4으로 나눈 값 만큼 돈다는 의미이다.
즉! 45도씩 돈다는 의미!! 이지만, 더 다이나믹한 애니메이션을 위해 3으로 나누면, 60도씩 도는 것을 알 수 있다.
 
 

하지만, 첫 클릭의 경우에만 애니메이션이 적용되고 끝나는 것을 알 수 있다.
 

 
클릭할 때 마다 애니메이션을 넣고 싶다..

let rotationAngle = CGFloat.pi / 2
        UIView.animate(withDuration: 0.3) {
//            self.floatingButton.transform = CGAffineTransform(rotationAngle: rotationAngle)
            self.floatingButton.transform = self.floatingButton.transform.rotated(by: rotationAngle)
        }

CGAffineTransform(rotationAngle: rotationAngle) 코드를
self.floatingButton.transform.rotated(by: rotationAngle) 이렇게 바꿔주었다.
 
 
CGAffineTransform(rotationAngle: rotationAngle)
 

기존 코드의 경우 주어진 회전 각도(rotationAngle)로 새로운 CGAffineTransform을 생성하고, 이를 **floatingButton**의 현재 변환에 대체한다. 즉, 이 코드를 사용하면 **floatingButton**의 이전 변환(크기 변경, 이동, 이전 회전 등)은 모두 무시되고 새로운 회전만 적용된다.

 
self.floatingButton.transform.rotated(by: rotationAngle)

반면, 수정한 코드는 **floatingButton**의 현재 변환에 회전을 추가한다. 이 코드를 사용하면 이전의 모든 변환이 유지되고, 그 위에 새로운 회전이 추가됩니다. 이렇게 하면 여러 변환을 순차적으로 적용할 수 있습니다. 따라서 첫 번째 코드는 '절대적인' 회전을 설정하고, 두 번째 코드는 '상대적인' 회전을 추가하는 것이라고 할 수 있다.

 
 
 

'iOS > UIKit' 카테고리의 다른 글

[iOS] Image contentmode의 모든 것  (2) 2023.06.25
[iOS] Initializer의 모든 것  (2) 2023.06.16
[iOS] CollectionView의 모든 것  (5) 2023.06.13
[iOS] Toggle() 을 활용한 버튼 클릭 이벤트  (0) 2023.06.08
[iOS] TableView의 모든 것  (4) 2023.06.06