- 가운데 십자 버튼을 누르면 3개의 플로팅 버튼이 등장하고, 다시 누르면 사라지도록 구현했습니다.
- 플로팅 버튼 관련 라이브러리도 많이 사용되고, 보통은 Stack View 의 isHidden 속성을 활용해서도 많이 구현합니다. Stack View 의 isHidden 은 차곡차곡 쌓이는 것처럼 보이게 하기 때문입니다.
- 하지만 저는 십자 버튼의 Super View 와 그 위의 image View 가 서로 constraint 가 걸려있기 때문에 Stack View 를 사용하기 까다로웠습니다. Stack 을 위로 열면 image View 도 같이 올라가 버렸기 때문에..
- 그래서 관련 라이브러리를 사용하지 않고, 직접 필요 사항에 맞춰 구현해봤습니다.
- imageView 의 super View 는 UIScrollView 였습니다. 확대 축소를 자유롭게 하기 위해서 입니다.
- 그래서 UIScrollView 의 frameLayoutGuide 를 활용해서 imageView 아래쪽에 3개의 버튼을 구성했습니다. 진짜로 십자 버튼이 있는 뷰에서 위로 출몰하는게 아니라, imageView 에서 투명하게 숨겨뒀다가 십자 버튼을 누르면 선명하게 나타나는 것처럼 보이게 했습니다.
- frameLayoutGuide 를 사용하면 UIScrollView 에서도 독립적으로 움직이지 않는 뷰를 구성할 수 있습니다.
- Snapkit 제약조건 설정
floatingView.snp.makeConstraints { make in make.left.right.bottom.equalTo(imageSuperScrollView.frameLayoutGuide) make.height.equalTo(100) }
- 버튼들을 담을 투명한 뷰를 floatingView 라고 이름지었습니다. 뷰를 생성할 때 주의점은 view. alpha = 1.0 으로 코드를 짜면 안된다는 겁니다.
let floatingView: UIStackView = { let view = UIStackView() // 이렇게 하면 안됨 ! view.alpha = 1.0 // ... return view }()
-> view.alpha = 1.0 으로 선언하면, 그 뷰에 담긴 객체들도 모두 투명하게 처리하기 때문입니다.
그래서 이렇게 코드를 짜는게 좋습니다.
let floatingView: UIStackView = { let view = UIStackView() // 이렇게 합시다 :) view.backgroundColor = UIColor(white: 1, alpha: 0) // ... return view }()
이렇게 하면 배경이 되는 floatingView 는 투명하게 되고, button 들도 제대로 보이게 됩니다.
- 애니메이션 효과는 UIView.animate() 을 사용했습니다.
// 십자 플로팅 버튼 @IBAction func tapBottomMiddleButton(_ sender: UIButton) { let gap = self.floatingLeftView.frame.width // 버튼들이 플로팅 되지 않은 상태이고, 움직이고 있지 않다면 if !areButtonsFloated && !areButtonsMoving { areButtonsMoving = true // copyButton self.copyButton.alpha = 0 self.copyButton.transform = CGAffineTransform(translationX: +gap, y: 0) UIView.animate(withDuration: 0.1) { self.copyButton.transform = CGAffineTransform(translationX: +(gap/2), y: 0) } UIView.animate(withDuration: 0.3) { self.copyButton.alpha = 1 self.copyButton.transform = CGAffineTransform(translationX: 0, y: 0) } // wifiButton self.wifiButton.alpha = 0 self.wifiButton.transform = CGAffineTransform(translationX: 0, y: +100) UIView.animate(withDuration: 0.3) { self.wifiButton.alpha = 1 self.wifiButton.transform = CGAffineTransform(translationX: 0, y: 0) } // shareButton self.shareButton.alpha = 0 self.shareButton.transform = CGAffineTransform(translationX: -gap, y: 0) UIView.animate(withDuration: 0.1) { self.shareButton.transform = CGAffineTransform(translationX: -(gap/2), y: 0) } UIView.animate(withDuration: 0.3, animations: { self.shareButton.alpha = 1 self.shareButton.transform = CGAffineTransform(translationX: 0, y: 0) }, completion: { finished in self.areButtonsFloated = true self.areButtonsMoving = false }) } // 버튼들이 플로팅 되어있고, 움직이고 있지 않다면 else if areButtonsFloated && !areButtonsMoving { areButtonsMoving = true // copyButton UIView.animate(withDuration: 0.1) { self.copyButton.transform = CGAffineTransform(translationX: +(gap/2), y: 0) } UIView.animate(withDuration: 0.3) { self.copyButton.alpha = 0 self.copyButton.transform = CGAffineTransform(translationX: +(gap), y: 0) } // wifiButton UIView.animate(withDuration: 0.3) { self.wifiButton.alpha = 0 self.wifiButton.transform = CGAffineTransform(translationX: 0, y: +100) } // shareButton UIView.animate(withDuration: 0.1) { self.shareButton.transform = CGAffineTransform(translationX: -(gap/2), y: 0) } UIView.animate(withDuration: 0.3, animations: { self.shareButton.alpha = 0 self.shareButton.transform = CGAffineTransform(translationX: -(gap), y: 0) }, completion: { finished in self.areButtonsFloated = false self.areButtonsMoving = false }) } }