ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Custom Stepper
    SwiftUI 2022. 1. 28. 19:53

    안녕하세요. 그린입니다🟢

    이번 포스팅에서는 커스텀한 Stepper를 구현해보겠습니다🙋🏻

     

    SwiftUI에서 기본 내장되어있는 Stepper가 있습니다.

    그런데..! NavigationLink를 통해 들어간 뷰에서는 왜인지 Stepper의 동작이 다소 이상합니다.

    예를들어 살짝 누르면 안되고 꾸욱 눌러주어야 변경되며 버튼 Hold 액션을 통해서는 반복적으로 카운팅이 안올라가는ㅠㅠ

    관련 간증들을 찾아보니 SwiftUI에서 NavigationLink의 파생된 버그 같더라구요..

    네비게이션 관련해서 아직까지 여러 버그가 있는데 그중 하나인것 같습니다🤬

     

    그래서..! 직접 Stepper를 동일하게 구현해봤습니다.

    Stepper 구현하기

    import SwiftUI
    
    struct ContentView: View {
      @State var count: Int = 1
      
      var body: some View {
        HStack(spacing: 0) {
          StepperView(
            title: "-",
            color: count == 1 ? .red : .gray
          ) {
            if count > 1 {
              count -= 1
            }
          }
          Rectangle().foregroundColor(.black)
            .frame(width: 1, height: 15)
          StepperView(title: "+") {
            count += 1
          }
        }
        .background(.yellow)
        .cornerRadius(10)
        
        Text("count is \(count)")
          .font(.largeTitle)
          .padding()
      }
    }
    
    private struct StepperView: View {
      @State private var timer: Timer?
      @State private var isLongPressing = false
      
      public var title: String
      public var color: Color = .gray
      public var action: () -> Void = {}
      
      public init(
        title: String,
        color: Color = .gray,
        action: @escaping () -> Void = {}
      ) {
        self.title = title
        self.color = color
        self.action = action
      }
      
      var body: some View {
        Button(
          action: {
            if self.isLongPressing {
              self.isLongPressing.toggle()
              self.timer?.invalidate()
            } else {
              self.action()
            }
          },
          label: {
            HStack(spacing: 0) {
              Text(title)
                .font(.largeTitle)
                .padding(.horizontal, 16)
                .padding(.vertical, 9)
            }
            .foregroundColor(color)
            .frame(height: 30)
          }
        )
          .padding(.horizontal, 30)
          .simultaneousGesture(
            LongPressGesture(minimumDuration: 0.5)
              .onEnded { _ in
                self.isLongPressing = true
                self.timer = Timer.scheduledTimer(
                  withTimeInterval: 0.1,
                  repeats: true,
                  block: { _ in
                    self.action()
                  }
                )
              }
          )
          .simultaneousGesture(
            DragGesture()
              .onEnded { _ in
                self.isLongPressing.toggle()
                self.timer?.invalidate()
              }
          )
      }
    }

    총 2개의 View로 간단히 구성해봤어요.

    우선 가장 중요한 StepperView를 보시죠.

     

    StepperView

    각 감소/증가에 대해서 가져가는 뷰입니다.

    우선 title/color/action을 이니셜라이즈 할 수 있도록 프로퍼티 설정 후 받습니다.

    내부 구현은 Button으로 구성했으며 3가지의 제스쳐를 취할 수 있습니다.

    1. TapGesture

    기본적으로 탭되었을때 하나씩 감소/증가 할 수 있도록 합니다.

    2. LongPressGesuture

    롱 프레스 0.5초 정도 꾹 눌렸을때 반복적으로 감소/증가될 수 있도록 구성하였어요.

    simultaneousGesture 메서드를 통해 동시에 해당할 수 있도록 설정합니다.

    timer와 롱 프레스인지 판단할 수 있는 isLongPress 프로퍼티를 선언합니다.

    그래서 롱 프레스 되었으면 isLongPress를 true해줍니다.

    그 다음 타이머를 통해 0.1초 간격으로 반복될 수 있도록 설정해줍니다.

    여기서 꾹 누른걸 떼는 순간 버튼의 탭으로 감지하여 isLongPress인지 아닌지 판단하여 isLongPress이면

    타이머를 무효시키고 isLongPress도 toggle해줍니다.

    그게 아니라면 그냥 한번씩 누르는 버튼의 액션이죠.

    3. DragGesture

    이건 꾹 누른 상태에서 해당 Stepper의 영역 밖으로 드래그 되었을때 이 구현이 없다면

    계속 카운팅이 올라가고 정상적이지 않게 됩니다.

    그래서 Stepper의 기본 구현과 동일하도록 이 제스쳐가 끝난다면 액션을 무효 시킬 수 있도록 설정해줍니다.

    그 외에 원하는 감소/증가에 대한 title과 color를 지정해줘요.

     

    ContentView

    이 뷰에서는 StepperView를 가져와 쓰면 됩니다.

    아 액션을 넘겨줄때 1보다 작으면 더 감소되지 않도록 조건 및 color를 바꿔주도록 했습니다.

    이건 쓰기 나름이니 패쓰~!!

    구현 영상

    마무리

    일단 스유를 사용하며 네비게이션 링크를 통해서는 좀 많은 버그들이 아직까진 있는 상태라..

    이럴때는 커스텀하게 직접 구현해서 사용하는 방법이 있어서 해봤습니다😭

    더 더 커스텀하게 사용할 수 있도록 다듬고 라이브러리화 시켜 깃헙에 저와 같은 문제를 겪는 분들께 공유하고

    사용할 수 있도록 해볼 예정입니다!😁

    우선 위의 코드는 제 깃헙에 올려놨습니다ㅎㅎ

    https://github.com/GREENOVER/playground/tree/main/custom-stepper

     

    GitHub - GREENOVER/playground: 학습을 하며 간단한 예제들을 만들어 보는 작고 소중한 놀이터

    학습을 하며 간단한 예제들을 만들어 보는 작고 소중한 놀이터. Contribute to GREENOVER/playground development by creating an account on GitHub.

    github.com

    'SwiftUI' 카테고리의 다른 글

    SwiftUI - TabView  (0) 2022.04.09
    SwiftUI - @State / @Binding / @StateObject / @EnvironmentObject  (0) 2022.03.21
    SwiftUI - multilineTextAlignment & lineLimit & lineSpacing  (0) 2022.01.19
    swipeActions  (0) 2021.12.23
    Tap&Drag Gesture in SwiftUI  (0) 2021.12.02
Designed by Tistory.