简体   繁体   中英

SwiftUI Navigation similar to Slack

I am trying to make a navigation UI similar to the Slack app where I have the Home Screen which Overlays the Menu Navigation screen. I created a ViewModifier which makes the Home Screen Draggable. Now I need to add functionality such that when the "Home" is tapped on the blue Menu screen, the white Home View animates back to the center. My idea was to keep track of the NavigationState in a global AppState:

enum NavigationSelection {
  case menu
  case home
}

final class AppState: ObservableObject {
  let objectWillChange = PassthroughSubject<Void, Never>()
  @Published var currentNavigationSelection: NavigationSelection = .home 
}

Then when the user taps "Home", have it update the AppState's currentNavigationSelection, and have the Draggable view determine its offset based on the currentNavigationSelection. I'm really not sure about this approach and I'm having a tough time thinking about it in the new SwiftUI style. Any suggestions would be much appreciated.

在此处输入图片说明

The view hierarchy looks like this:

 var body: some View {
    ZStack {
        Menu()
        HomeTabView()
    }
}

And the HomeTabView has a draggable ViewModifier applied:

struct Slidable: ViewModifier {

  @EnvironmentObject var app: AppState

  @State private var viewState = SlidableViewDragState.normal.defaultPosition
  @State private var currentPosition: SlidableViewDragState = .normal {
    didSet {
        self.viewState = self.currentPosition.defaultPosition
    }
  }

  func body(content: Content) -> some View {
    return content
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight:
        .infinity, alignment: Alignment.topLeading)
        .offset(self.viewState)
        .animation(.interactiveSpring())
    .gesture(
        DragGesture()
            .onChanged({ (value) in
                self.viewState = self.currentPosition.applyXTranslation(x: value.translation.width)
            })
            .onEnded({ (value) in
                if value.translation.width < 0 && self.currentPosition == .normal {
                    return
                }
                if abs(value.translation.width) > self.currentPosition.switchThreshold {
                    self.currentPosition = self.currentPosition.oppositePosition
                    if self.currentPosition == .menuVisible {
                        self.app.currentNavigationSelection = .menu
                    }
                } else {
                    self.viewState = self.currentPosition.defaultPosition
                }
            })
    )
  }
}

Moving both views

How do you currently define the positions of both? With my limited experience I would use a ZStack embedding HomeView and MenuView . This way you can move the views around independently.

Then you use a point variable as state, and make the DragGesture set point . Then you determine at the end of the drag what end position point is set to.

point is part of the offset-calculation. You can calculate the menu-offset with .offset(x: point.x) and the home-offset with .offset(x: -Self.maxOffset - Self.minusHomeWidth / 2 + point.x) .

minusHomeWidth is the width the menu still shows when you are on the home screen.

Variables defining min and max of the point :

static let minOffset: CGFloat = 0
static let maxOffset: CGFloat = UIScreen.main.bounds.width - Self.minusHomeWidth
static let minusHomeWidth: CGFloat = UIScreen.main.bounds.width / 10

Then you can make it move to the home-view width

Button(action: {
    self.point = CGPoint(x: Self.maxOffset, y: 0)
}) { Text("Go to Home") }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM