0
2

More than 1 year has passed since last update.

スライドすると隠れたボタンが現れる View の作成方法 in SwiftUI

Posted at

概要

iOS において、左にスライドすると、右から隠れたボタンが現れる View の Swift UI での実装方法について紹介します。

output2.gif

実装方法

ユーザがボタンをドラッグするのに合わせ、 View の x 軸のオフセットを変更することで、このような View を実現することができます。

Xcode の Playground で実行可能なコードは以下のとおりです。

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    var body: some View {
        VStack(spacing: 0) {
            SlideButtons()
                .frame(width: 300, height: 40)
            SlideButtons()
                .frame(width: 300, height: 40)
            SlideButtons()
                .frame(width: 300, height: 40)
        }
    }
}

struct SlideButtons: View {
    // The current x offset of the view.
    @State private var xOffset = CGFloat.zero
    
    // The default x offset when a drag ends.
    @State private var defaultXOffset = CGFloat.zero
    
    // Initialize it on `.onAppear()`.
    @State private var hiddenButtonWidth: CGFloat = 0
    
    @State private var hiddenButtonMaxWidth: CGFloat = 50
    
    // Ratio of a hidden button width to the entire View.
    private let buttonSizeRatio = 6
    
    private var dragGesture: some Gesture {
        DragGesture(minimumDistance: 30, coordinateSpace: .local)
            .onChanged { value in
                self.xOffset += value.translation.width
                if self.hiddenButtonMaxWidth * -2 <= self.xOffset && self.xOffset <= 0 {
                    self.hiddenButtonWidth = min(self.xOffset / -2, self.hiddenButtonMaxWidth)
                }
            }
            .onEnded { value in
                withAnimation() {
                    if self.areHiddenButtonsShowed() {
                        if (self.xOffset - self.defaultXOffset) > self.getMinDistanceToChangeDefaultXOffset() {
                            self.hideButtons()
                        } else {
                            // Return to the current default position.
                            self.xOffset = self.defaultXOffset
                            // Reset the button size
                            self.hiddenButtonWidth = self.hiddenButtonMaxWidth
                        }
                        
                    } else {
                        if (self.defaultXOffset - self.xOffset) > self.getMinDistanceToChangeDefaultXOffset() {
                            self.showHiddenButtons()
                        } else {
                            // Return to the current default position.
                            self.xOffset = self.defaultXOffset
                            // Reset the button size
                            self.hiddenButtonWidth = 0
                        }
                    }
                }
            }
    }
    
    var body: some View {
        GeometryReader { geometry in
            HStack(spacing: 0) {
                // Main button
                Rectangle()
                    .foregroundColor(.black)
                    .overlay {
                        HStack {
                            Text("Read a book")
                                .foregroundColor(.white)
                                .padding()
                            Spacer()
                        }
                    }
                    .frame(width: geometry.size.width,
                           height: geometry.size.height)
                    .gesture(
                        self.dragGesture
                    )
                    .onTapGesture {
                        withAnimation() {
                            if self.areHiddenButtonsShowed() {
                                self.hideButtons()
                            } else {
                                self.showHiddenButtons()
                            }
                        }
                    }
                
                // 1st hidden button
                Rectangle()
                    .frame(width: self.hiddenButtonWidth, height: geometry.size.height)
                    .foregroundColor(Color.yellow)
                    .overlay {
                        Text("Skip")
                            .foregroundColor(.white)
                    }
                
                // 2nd hidden button
                Rectangle()
                    .frame(width: self.hiddenButtonWidth, height: geometry.size.height)
                    .foregroundColor(Color.red)
                    .overlay {
                        Text("Done")
                            .foregroundColor(.white)
                    }
            }
            .onAppear() {
                self.hiddenButtonWidth = 0
                self.hiddenButtonMaxWidth = geometry.size.width / CGFloat(self.buttonSizeRatio)
                self.hideButtons()
            }
            .frame(width: geometry.size.width, height: geometry.size.height, alignment: .leading)
            .offset(x: self.xOffset)
        }
    }
    
    private func areHiddenButtonsShowed() -> Bool {
        return self.defaultXOffset == self.getXOffsetToShowHiddenButtons()
    }
    
    private func hideButtons() {
        self.hiddenButtonWidth = 0
        self.xOffset = CGFloat.zero
        self.defaultXOffset = .zero
    }
    
    private func getMinDistanceToChangeDefaultXOffset() -> CGFloat {
        return self.hiddenButtonMaxWidth / 2
    }
    
    private func getXOffsetToShowHiddenButtons() -> CGFloat {
        return (self.hiddenButtonMaxWidth * 2) * -1
    }
    
    private func showHiddenButtons() {
        self.hiddenButtonWidth = self.hiddenButtonMaxWidth
        self.xOffset = self.getXOffsetToShowHiddenButtons()
        self.defaultXOffset = self.getXOffsetToShowHiddenButtons()
    }
}

PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())


参考

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2