I am trying to implement Uber trip like screen, and I have used the code below
public struct OverlappingPanView<BGView, PanContentView>: View
where BGView: View, PanContentView: View {
public var backgroundView: () -> BGView
public var panContentView: () -> PanContentView
public init(backgroundView: @escaping () -> BGView, panContentView: @escaping () -> PanContentView) {
self.backgroundView = backgroundView
self.panContentView = panContentView
}
enum OffsetError : Error{
case TopBoundExceded
case BottomBoundExceded
}
//MARK: - State Property Variables
@GestureState private var dragOffset: CGFloat = 0
@State private var lastEndedOffset: CGFloat = 0
@State private var panFrame: CGSize = .zero
//MARK: - Getters
var staticTopPadding: CGFloat{
let screenPadding = UIScreen.main.bounds.height * 0.4
let actualFrame = (panFrame.height - screenPadding)
let extraCorrection = actualFrame * 0.5
return screenPadding + extraCorrection
}
private var currentPaddingValue: CGFloat{
return dragOffset + lastEndedOffset + staticTopPadding
}
private var difference: CGFloat{
return 200
}
//MARK: - Body
public var body: some View {
ZStack {
backgroundView()
.edgesIgnoringSafeArea(.all)
panControllerView()
}
}
func panControllerView() -> some View{
panContentView()
.background(
GeometryReader { geo -> Color in
DispatchQueue.main.async {
self.panFrame = geo.size
}
return Color.clear
}
)
.frame(minHeight: 0, maxHeight: .infinity)
.offset(y: dragOffset + lastEndedOffset + staticTopPadding)
.animation(.interactiveSpring())
.highPriorityGesture(
DragGesture(coordinateSpace: CoordinateSpace.global)
.updating($dragOffset) { (value, state, _) in
if let endValue = try? self.canDrag(value.translation.height){
state = endValue
}
}
.onEnded { value in
self.onDragEnd(value.translation.height)
}
)
}
func onDragEnd(_ value: CGFloat){
withAnimation {
do{
let endValue = try self.canDrag(value)
self.lastEndedOffset = lastEndedOffset + endValue
}catch OffsetError.BottomBoundExceded{
self.lastEndedOffset = 0
}catch OffsetError.TopBoundExceded{
self.lastEndedOffset = -(self.panFrame.height - difference)
}catch{
self.lastEndedOffset = 0
}
}
}
func canDrag(_ value: CGFloat) throws -> CGFloat{
let newValue = value + lastEndedOffset + staticTopPadding
// stop going below
guard newValue <= staticTopPadding else{
throw OffsetError.BottomBoundExceded
}
let topCalculation = (self.panFrame.height + (value + self.lastEndedOffset)) - difference
// stop going top
guard topCalculation >= 0 else{
throw OffsetError.TopBoundExceded
}
return value
}
}
and Used the above code to show mapview in the background and ForEach content on the front.
OverlappingPanView {
GoogleMapView(delegate: presenter)
} panContentView: {
contentView() // ForEach looped data Views
}
The list that I have provided in content View is leaking memory and uses high CPU (above 100%) when dragging leading to UI Performance issues.