djk3000 / ME

4 stars 2 forks source link

SwiftUI-半屏底部弹出框自定义实现(ViewBuilder应用) #90

Open djk3000 opened 2 years ago

djk3000 commented 2 years ago

起因是我希望这个框能够想sheet一样复用,sheet只能弹到顶部,ios16才有半屏的弹出框,所以就只能自己写一个自定义控件。 在这里就需要用到@ ViewBuilder来实现这个功能,他允许闭包中提供多个子视图,可以反复套用,将填充内容填进去。 就像原生控件Button一样 Button { //action } label: { //Conten } 这个label里面就可以自己填想要的Content,可以存放任何Content。

先上代码:

import SwiftUI

struct BottomSheet<Content: View>: View {
    var sheetHeight: CGFloat = 420
    @State var safeBottom: CGFloat = 0.0
    @State var startingOffset: CGFloat = UIScreen.main.bounds.size.height
    @State var currentOffset: CGFloat = 0.0

    @Binding var isShow: Bool
    @ViewBuilder var content: Content

    var body: some View {
        ZStack {
            if isShow{
                Color("Button_PointOut")
                    .zIndex(1)
                    .onTapGesture {
                        withAnimation {
                            startingOffset = UIScreen.main.bounds.size.height
                            isShow = false
                        }
                    }
            }

            ZStack(alignment: .top) {
                Color.clear
                ZStack{
                    content
                }
                .frame(maxWidth: .infinity, maxHeight: safeBottom + sheetHeight)
                .background(Color.white)
                .onAppear{
                    safeBottom = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0.0
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .cornerRadius(12)
            .zIndex(2)
            .offset(y: startingOffset)
            .offset(y: currentOffset)
            .gesture(
                DragGesture()
                    .onChanged({ value in
                        if currentOffset < 0{
                            return
                        }
                        withAnimation(.spring()){
                            currentOffset = value.translation.height
                        }
                    })
                    .onEnded({ value in
                        withAnimation(.spring()){
                            if currentOffset > 100{
                                startingOffset = UIScreen.main.bounds.size.height
                                isShow = false
                            }
                            currentOffset = 0
                        }
                    })
            )
        }
        .onChange(of: isShow) { newValue in
            withAnimation {
                startingOffset = isShow ? startingOffset - safeBottom - sheetHeight : UIScreen.main.bounds.size.height
            }
        }
    }
}

这里可以通过控制sheet的高度,然后点击按钮显示sheet,同时传递任意的Content进来 这里还是参考我之前写的那个手势拖拽的文章。

然后使用的地方:

  BottomSheet(sheetHeight: 370, isShow: $showSheet){
         //Content
   }

参考文档