SwiftUI로 무작정 만들기 1탄, 플로팅 버튼과 커스텀 시트

해류·2022년 5월 18일
0
post-thumbnail

들어가기에 앞서서

코딩 공부를 시작해보니 클론 코딩이 그렇게 안좋다고 말은 많아도 클론 코딩을 안 할 수는 없더라.

(초보자 입장에서 구글 검색이나 스택스에서 찾아도 이해가 어렵다. 저게 왜 쓰였는지는 둘째치고 코딩이 설계된 순서랄까, 작성자의 생각이 읽어지지 않아서 너무 힘들었다. 결국 그래서 유튜브로 공부 중이다.)

그래서 할 거면 완벽히 이해하고 다시 써먹을 수 있을 정도로 샅샅이 분해해서 이해해보자. 그렇게 생각하고 일단 필자도 어떻게 공부할지 몰라 클론 코딩으로 공부하고 원하는 기능만 뽑아내서 하나씩 만들어 보는 중이다.

시작해보자!

플로팅 버튼 한번 만들어 보려다 이왕 만든 버튼에 시트를 달아 주었다. 그런데 뭔가 아쉬워서 커스텀 시트까지 만들어 보았다.

#FloatingButton #CustomSheet #HalfSheet #BottomSheet

결과물

비디오로 보려면 여기를 클릭 :

애니메이션이 들어가서 그런가 생각보다 그럴싸하다.

버튼은 일반 클릭과 길게 눌렀을 때의 액션을 다르게 설정해주었다.

마이크 시트는 기존 시트 기능을 이용해서 뷰를 불러왔고 닫기 버튼만 추가 해줬다.

키보드 시트가 드래그에 따라 이동하면서 절반 정도와 하단에 스냅 되도록 만들었다.


플로팅 버튼 구현하기

1. ButtonStyle

ButtonStyle을 이용하면 간단하게 버튼의 디자인하거나 버튼 디자인을 만들어 획일하게 만들어 낼 수 있다.

func buttonStyle<S>(_ style: S) -> some View where S : ButtonStyle

여기선 이모티콘의 크기가 달라 획일화가 불가능하기 때문에 버튼 자체를 커스텀 하지 않고 공통되는 버튼색, 배경, 모양, 버튼효과만 따로 추가해 생성했다. 특히, 버튼 이펙트를 지정하려면 configuration을 받아와야 isPressed를 사용할 수 있기에 버튼 스타일을 사용한 것.

struct TapShrinkButton: ButtonStyle {
    //.label은 버튼을 감싼다고 생각하면 된다. 이 자리에 버튼 자체르 삽입해서 획일화 가능.
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .background(Color.blue)
            .foregroundColor(.white)
            .clipShape(Circle())
            .scaleEffect(configuration.isPressed ? 0.9 : 1) << 이것 때문에 버튼 스타일 사용
    }
}

2. LongPressGesture

gesture는 .gesture()와 .simultaneousGesure로 사용 가능한데, .gesture()에 .simultaneousGesure로 두가지 제스쳐를 혼합할 수도 있다.

여기선 버튼이기 때문에 simultaneousGesure로 버튼과 합쳐 사용되도록 구성했다.

Button	
//버튼에 롱프레스 제스쳐를 첨가해서 액션을 추가
.simultaneousGesture(LongPressGesture(minimumDuration: 0.5)
    .onEnded{_ in
        isLongPressing = true
        withAnimation(.interactiveSpring(response: 0.5, dampingFraction: 0.8)) {
   		    show.toggle()
        }
        //확인용
        print("Pressing")
    }
)

3. withAnimation

withAnimaiton은 동작의 트리거가 되는 대상을 감싸주면 그와 연계되서 일어나는 변화에 애니메이션을 주게 된다.

여기선 showKeyboard라는 변수가 트리거가 되는 행동이 전부 해당된다.

//이 버튼으로 활성화되는 모든 동작에 대해 애니메이션 영향을 받는다.
//여기선 숨겨있던 버튼들이 나타나고 화살표 모양이 돌아가는 모션이다.
withAnimation(.easeInOut) {
    showKeybord = true
}

커스텀 시트 구현하기

1. GeometryReader

GeometryReader는 부모뷰가 자식뷰에 배정하는 화면 영역에 대한 정보(Size와 SafetyArea)를 다룬다.

//GeometryReader가 감싸고 있는 뷰가 사용하고 있는 영역 정보를 이용
GeometryReader { proxy in
//이런 식으로 VStack이 사용 중인 높이와 너비를 가져올 수 있다.
let height = proxy.size.height
let height = proxy.size.width
	VStack {
		Color.white.ignoresSafeArea()
	}
}

2. DragGesture

드래그 사용시 관련된 정보를 가져오거나 행동을 추가 할 수 있는 수정자이다.

여기선 이 드래그 제스쳐를 이용해 뷰가 중단과 하단에 스냅 되도록 만들었다.

//아래에서 위로 스크롤할 때 뷰가 올라가지 않도록 설정.
.offset(y: (translation.height + offsetY) > 0 ? translation.height + offsetY : 0)
.gesture(
    DragGesture()
        .onChanged { value in
            translation = value.translation
        }
        .onEnded { value in
            withAnimation(.interactiveSpring(response: 0.5, dampingFraction: 0.8)) {
                let snap = translation.height + offsetY
                let quarter = proxy.size.height/4

                if snap > quarter && snap < quarter*3 {
                    offsetY = quarter*2
                } else if snap > quarter*3 {
                    offsetY = quarter*4 - 50
                } else {
                    offsetY = 0
                }
                translation = .zero
            }
        }

전체코드 : https://github.com/dduruge/SwiftUI_Test/tree/master/Swiftui/FloatingButtonWithCustomSheet


생각

코딩 공부를 늦게 시작했고, 한 지도 얼마 되지 않아서 항상 고민이 많다. 일단은 흥미 위주로 찾아보고 만들고 내가 이해할 수 있는 코드 변경하고 새로 만들 수 있는 코드를 짜려고 노력하고 있다. 그러면서 드는 생각은 내가 짜는 이 코드가 앱 자체를 무겁게 만들지는 않는지, 내가 잘하고 있는게 맞는지, 다른 사람도 보고 이해할 수 있을지 여부가 궁금하다. 그래서 이렇게 깃허브도 처음 올려보고 블로그도 처음 써본다. 빨리 실력 늘어서 나만의 앱도 만들어보고 취업도 해야지. 자잘한건 올릴생각이 없어서 일주일에 하나씩 글을 써보려고 생각 중이다. 미래의 나야.. 잘 할 수 있지?

profile
iOS 개발자 지망생

0개의 댓글