Skip to content
Merged
1 change: 0 additions & 1 deletion Runnect-iOS/Runnect-iOS/Global/Base/BaseView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@ class BaseView: UIView, BaseViewProtocol {

func setLayout() { }
}

162 changes: 86 additions & 76 deletions Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomBottomSheetVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ enum SheetType {

final class CustomBottomSheetVC: UIViewController {
// MARK: - Properties
private let backgroundView = UIView().then {
$0.backgroundColor = .black.withAlphaComponent(0.65)
}
private let titleNameMaxLength = 20
private var bottomSheetType: SheetType!

var backgroundTapAction: (() -> Void)?
var completeButtonTapAction: ((String) -> Void)?

// 바텀 시트 높이
let bottomHeight: CGFloat = 206
private let titleNameMaxLength = 20
private let bottomHeight: CGFloat = 206
private let backgroundView = UIView().then { $0.backgroundColor = .g1.withAlphaComponent(0.6) }

private var cancelBag = CancelBag()
private var bottomSheetType: SheetType!

// MARK: - UI Components

private let bottomSheetView = UIView().then {
$0.backgroundColor = .w1
$0.layer.cornerRadius = 20
Expand Down Expand Up @@ -65,6 +65,7 @@ final class CustomBottomSheetVC: UIViewController {
}

// MARK: - Initialization

init(type: SheetType) {
super.init(nibName: nil, bundle: nil)
self.bottomSheetType = type
Expand All @@ -75,21 +76,22 @@ final class CustomBottomSheetVC: UIViewController {
}

// MARK: - View Life Cycle

override func viewDidLoad() {
super.viewDidLoad()
self.setUI()
self.setLayout(bottomSheetType)
self.setDelegate()
self.setTapGesture()
self.setAddTarget()
self.setBinding()
self.showBottomSheet()
if bottomSheetType == .textField {
showBottomSheet()
setupGestureRecognizer()
self.setGesture()
self.setAddTarget()
}
}

// MARK: - Methods

@discardableResult
func setContentsText(text: String) -> Self {
contentsLabel.text = text
Expand All @@ -112,21 +114,6 @@ final class CustomBottomSheetVC: UIViewController {
bottomSheetTextField.delegate = self
}

private func dismissBottomSheet() {
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}

private func setTapGesture() {
let tap = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleBackgroundTap))
backgroundView.addGestureRecognizer(tapGesture)
}

private func setAddTarget() {
NotificationCenter.default.addObserver(
self,
Expand All @@ -144,6 +131,7 @@ final class CustomBottomSheetVC: UIViewController {
}

// MARK: - UI & Layout

extension CustomBottomSheetVC {
private func setUI() {
view.addSubview(backgroundView)
Expand Down Expand Up @@ -199,7 +187,6 @@ extension CustomBottomSheetVC {
bottomSheetView.snp.makeConstraints { make in
make.leading.bottom.trailing.equalToSuperview()
make.top.equalTo(view.snp.top).offset(topConst)
make.height.equalTo(bottomHeight)
}

dismissIndicatorView.snp.makeConstraints { make in
Expand All @@ -215,7 +202,6 @@ extension CustomBottomSheetVC {
}

bottomSheetTextField.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(contentsLabel.snp.bottom).offset(19)
make.leading.trailing.equalToSuperview().inset(16)
make.height.equalTo(44)
Expand Down Expand Up @@ -245,64 +231,40 @@ extension CustomBottomSheetVC {
bottomSheetView.snp.remakeConstraints { make in
make.leading.bottom.trailing.equalToSuperview()
make.top.equalTo(view.snp.top).offset(topConst)
make.height.equalTo(bottomHeight)
}

UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: {
self.backgroundView.alpha = 0.65
self.view.layoutIfNeeded()
}, completion: nil)
}

private func hideBottomSheetAndGoBack() {
let safeAreaHeight = view.safeAreaLayoutGuide.layoutFrame.height
let bottomPadding = view.safeAreaInsets.bottom

let topConst = (safeAreaHeight + bottomPadding)

bottomSheetView.snp.remakeConstraints { make in
make.leading.bottom.trailing.equalToSuperview()
make.top.equalTo(view.snp.top).offset(topConst)
make.height.equalTo(bottomHeight)
}

UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: {
self.backgroundView.alpha = 0.0
self.view.layoutIfNeeded()
}) { _ in
if self.presentingViewController != nil {
self.dismiss(animated: false, completion: nil)
}
}
}

private func setupGestureRecognizer() {
private func setGesture() {
let dimmedTap = UITapGestureRecognizer(target: self, action: #selector(dimmedViewTapped(_:)))
backgroundView.addGestureRecognizer(dimmedTap)
backgroundView.isUserInteractionEnabled = true

let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(panGesture))
swipeGesture.direction = .down
view.addGestureRecognizer(swipeGesture)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(_:)))
bottomSheetView.addGestureRecognizer(panGesture)
}
}

// MARK: - @objc Function

extension CustomBottomSheetVC {
@objc private func keyboardWillShow(_ sender: Notification) {
self.view.frame.origin.y = -291
}

@objc private func keyboardWillHide(_ sender: Notification) {
self.view.frame.origin.y = 0
}

@objc private func endEditing() {
bottomSheetTextField.resignFirstResponder()
@objc private func keyboardWillShow(_ sender: Notification) { // 키보드의 높이만큼 화면을 올려줍니다.
if let keyboardFrame: NSValue = sender.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
self.view.frame.origin.y -= keyboardHeight
}
}

@objc private func handleBackgroundTap() {
dismissBottomSheet()
@objc private func keyboardWillHide(_ sender: Notification) { // 키보드의 높이만큼 화면을 내려줍니다.
if let keyboardFrame: NSValue = sender.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
self.view.frame.origin.y += keyboardHeight
}
}

@objc private func textFieldTextDidChange() {
Expand All @@ -320,22 +282,70 @@ extension CustomBottomSheetVC {
}

@objc private func dimmedViewTapped(_ tapRecognizer: UITapGestureRecognizer) {
hideBottomSheetAndGoBack()
let safeAreaHeight = view.safeAreaLayoutGuide.layoutFrame.height
let bottomPadding = view.safeAreaInsets.bottom

let topConst = (safeAreaHeight + bottomPadding)

bottomSheetView.snp.remakeConstraints { make in
make.leading.bottom.trailing.equalToSuperview()
make.top.equalTo(view.snp.top).offset(topConst)
}

UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: {
self.backgroundView.alpha = 0
self.view.layoutIfNeeded()
}, completion: { _ in
if self.presentingViewController != nil {
self.dismiss(animated: true, completion: nil)
}
}) // 하나 이상의 클로저 인수를 전달할때, 후행 클로저 구문을 사용하면 안된다는 경고로 일부 수정

}

@objc func panGesture(_ recognizer: UISwipeGestureRecognizer) {
if recognizer.state == .ended {
switch recognizer.direction {
case .down:
hideBottomSheetAndGoBack()
default:
break
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {

let translation = recognizer.translation(in: bottomSheetView)
let velocity = recognizer.velocity(in: bottomSheetView)

switch recognizer.state {
case .began:
backgroundView.alpha = 0 // 시작할때 드래그 시작할때 alpha 값 0
recognizer.setTranslation(CGPoint.zero, in: bottomSheetView)
case .changed:
if velocity.y > 0 && translation.y > 0 { // 아래로만 Pan 가능한 로직 , 여기서 translation.y > 0 이거 추가 안해주면 재밌게 에니메이션이 움직인다..
backgroundView.alpha = 0
UIView.animate(withDuration: 0.25, animations: {
self.view.transform = CGAffineTransform(translationX: 0, y: translation.y)
})
} else if velocity.y < 0 && translation.y > 0 {
// 위로도 움직이게는 하지만, 한계설정을 바텀시트의 높이로 설정, 즉 위쪽으로 하나도 못 움직이게 설정 ( translation.y > 0 ) 한계점 설정
UIView.animate(withDuration: 0.25, animations: {
self.view.transform = CGAffineTransform(translationX: 0, y: translation.y)
})
}
case .ended:
// 끝나는 지점의 translation.y 값이 75보다 작으면(작게 이동 시) 뷰의 위치를 다시 원상복구하겠다. = 즉, 다시 y=0인 지점으로 리셋
if translation.y < 75 {
UIView.animate(withDuration: 0.25, animations: {
self.view.transform = .identity
}, completion: { _ in
// 애니메이션이 끝나면 실행될 코드
self.backgroundView.alpha = 0.6
})
recognizer.setTranslation(CGPoint.zero, in: bottomSheetView)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 무슨 코드인가요 ? 궁금해여

Copy link
Collaborator Author

@thingineeer thingineeer Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UIPanGestureRecognizer의 Action메소드가 호출되면, translation은 마지막으로 손가락을 이동한 위치에서부터 계산되지 않고, 손가락을 끌기 시작한 원래 장소!! (즉, 처음 PanGesture를 시작한 위치)에서 계산됩니다.

recognizer.setTranslation(CGPoint.zero, in: bottomSheetView)

그래서 우리는 위 메소드를 0.0으로 호출하여 UIPanGestureRecognizer에게 새로운 드래그 동작을 시작할 것임을 알리는 것이죠.
우리가 드래그를 이어서 한다고 해서 드래그가 한~~~번~~ 이런게 아닙니다. 늘 지속 됩니다 드래그는 ‼️
UIPanGestureRecognizerAction메소드는 지속적이라고 그랬죠? 계속 Action메소드를 호출하게 됩니다.
한번 Action메소드가 호출될때마다 파라미터로 보내는 view의 translation0,0으로 setting 해줘야만하는것이죠.

안그러면 우리가 처음 드래그를 시작한 그 위치!!!에서 부터 계속 거리를 계산할것이니까요. 

이해가 안된다면 해당 개념을 참고한 블로그에서 가져온 글이 있는데 참고하면 좋을 것 같습니다.!!

https://zeddios.tistory.com/356

} else { // translation.y 75 이상이면 해당 화면 dismiss 직접 사용해보니 적절한 값이 75라고 판단
self.backgroundView.alpha = 0
dismiss(animated: true, completion: nil)
}
default:
break
}
}
}

// MARK: - UITextFieldDelegate

extension CustomBottomSheetVC: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ extension CourseDrawingVC {
guard handleVisitor() else { return }
self.courseName = text
self.mapView.capturePathImage()
self.dismiss(animated: true)
self.dismiss(animated: false)
}
self.present(bottomSheetVC, animated: false)
}
Expand Down Expand Up @@ -342,7 +342,7 @@ extension CourseDrawingVC {

if SelectedInfo.shared.type == .map {
self.aboutMapNoticeView.addSubview(aboutMapNoticeLabel)
self.naviBarContainerStackView.addArrangedSubviews(underlineView,aboutMapNoticeView)
self.naviBarContainerStackView.addArrangedSubviews(underlineView, aboutMapNoticeView)

underlineView.snp.makeConstraints {
$0.leading.trailing.equalToSuperview()
Expand Down Expand Up @@ -392,7 +392,7 @@ extension CourseDrawingVC {
make.trailing.equalTo(view.safeAreaLayoutGuide)
make.top.equalTo(view.snp.bottom)
}

completeButton.snp.makeConstraints { make in
make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16)
make.height.equalTo(44)
Expand Down Expand Up @@ -432,11 +432,11 @@ extension CourseDrawingVC {
guard let departureLocationModel = self.departureLocationModel else { return nil }
let path = mapView.getMarkersLatLng().map { $0.toRNLocationModel() }
let courseDrawingRequestData = CourseDrawingRequestData(path: path,
// title : self.courseName,
// title : self.courseName,
distance: self.distance,
departureAddress: departureLocationModel.departureAddress,
departureName: departureLocationModel.departureName)

let courseDrawingRequestDto = CourseDrawingRequestDto(image: imageData, data: courseDrawingRequestData)

return courseDrawingRequestDto
Expand Down Expand Up @@ -497,4 +497,3 @@ extension CourseDrawingVC {
}
}
}