Incorrect height calculation with DragGesture() in SwiftUI
Here is an approach (some helpful comments in code below)
struct TestResizingCard: View {
static let kMinHeight: CGFloat = 100.0
@State var currentHeight: CGFloat = kMinHeight // << any initial
var body: some View {
GeometryReader { g in // << for top container height limit
ZStack(alignment: .bottom) {
Rectangle().fill(Color.yellow) // << just for demo
self.card()
.gesture(DragGesture()
.onChanged { value in
// as card is at bottom the offset is reversed
let newHeight = self.currentHeight - (value.location.y - value.startLocation.y)
if newHeight > Self.kMinHeight && newHeight < g.size.height {
self.currentHeight = newHeight
}
})
}
}
}
func card() -> some View {
ZStack(alignment: .top){
RoundedRectangle(cornerRadius: 16.0)
.frame(height:currentHeight)
Text("Card")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.padding(.top)
}
}
}
struct TestResizingCard_Previews: PreviewProvider {
static var previews: some View {
TestResizingCard()
}
}
How to change the height of the object by using DragGesture in SwiftUI?
The correct calculation is
.gesture(
DragGesture()
.onChanged { value in
height = max(MIN_HEIGHT, height + value.translation.height)
}
)
Also, remove the infinite width. It's invalid.
.frame(minHeight: 0, maxHeight: height)
or
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: height)
Limit rectangle to screen edge on drag gesture
Here is a demo of possible approach (for any view, cause view frame is read dynamically).
Demo & tested with Xcode 12 / iOS 14
struct ViewSizeKey: PreferenceKey {
static var defaultValue = CGSize.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue()
}
}
struct ContentView: View {
@State private var pogPosition = CGPoint()
@State private var size = CGSize.zero
var body: some View {
GeometryReader { gp in
PogSound()
.background(GeometryReader {
Color.clear
.preference(key: ViewSizeKey.self, value: $0.frame(in: .local).size)
})
.onPreferenceChange(ViewSizeKey.self) {
self.size = $0
}
.position(pogPosition)
.gesture(
DragGesture()
.onChanged { value in
let rect = gp.frame(in: .local)
.inset(by: UIEdgeInsets(top: size.height / 2.0, left: size.width / 2.0, bottom: size.height / 2.0, right: size.width / 2.0))
if rect.contains(value.location) {
self.pogPosition = value.location
}
}
.onEnded { value in
print(value.location)
}
)
.onAppear {
let rect = gp.frame(in: .local)
self.pogPosition = CGPoint(x: rect.midX, y: rect.midY)
}
}.edgesIgnoringSafeArea(.all)
}
}
How to drag view for expand and collaps in SwiftUI?
Here is something I did once. Today you might want to do it with PreferenceKey.
struct ContentView: View {
let minHeight: CGFloat = 50
let startHeight: CGFloat = 50
@State private var currentHeight: CGFloat = 1000
@State private var contentHeight: CGFloat = 0
var textContent: some View {
VStack(alignment: .leading) {
Text("Text 1: orem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient.")
Text("Text 2: montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. ")
Text("Text 3: orem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur. ")
Text("Text 4: orem ipsum dolor sit amet, consectetuer adipiscing elit. Aene.")
Text("Text 5: orem ipsum dosque eu, pretium quis, sem. Nulla consequat massa quis enim. ")
Text("Text 6: orem ipsum dolor sit amet, consectetuer adipiscing elit. Aene.")
}
.padding()
}
var body: some View {
VStack {
VStack(alignment: .leading, spacing: 0) {
VStack(spacing: 0) {
Color.clear
.ignoresSafeArea()
.overlay(
textContent
.fixedSize(horizontal: false, vertical: true)
.overlay( GeometryReader { geo in Color.clear.onAppear {
contentHeight = geo.size.height
currentHeight = startHeight
}})
, alignment: .topLeading )
.clipped()
Spacer(minLength: 0)
}
.frame(height: currentHeight)
Button {
currentHeight = max(contentHeight, minHeight)
} label: {
Image(systemName: "rectangle.arrowtriangle.2.outward")
.padding(.horizontal)
}
.frame(maxWidth: .infinity, alignment: .leading)
.overlay(
Image(systemName: "line.3.horizontal").foregroundColor(.secondary)
.contentShape(Rectangle())
.gesture(DragGesture()
.onChanged { value in
currentHeight += value.translation.height
currentHeight = max(currentHeight, minHeight)
}
.onEnded { value in
currentHeight = max(currentHeight, minHeight)
}
)
)
.padding(5)
}
.background(.gray)
.padding(.horizontal)
Spacer()
}
}
}
SwiftUI DragGesture only in one direction
Yes, it is, by applying one of the two components (either horizontal or vertical) of the gesture translation to the view offset.
Here's such behaviour implemented as a ViewModifier
.
struct DraggableModifier : ViewModifier {
enum Direction {
case vertical
case horizontal
}
let direction: Direction
@State private var draggedOffset: CGSize = .zero
func body(content: Content) -> some View {
content
.offset(
CGSize(width: direction == .vertical ? 0 : draggedOffset.width,
height: direction == .horizontal ? 0 : draggedOffset.height)
)
.gesture(
DragGesture()
.onChanged { value in
self.draggedOffset = value.translation
}
.onEnded { value in
self.draggedOffset = .zero
}
)
}
}
Demo:
struct ContentView: View {
var body: some View {
VStack {
Spacer(minLength: 100)
HStack {
Rectangle()
.foregroundColor(.green)
.frame(width: 100, height: 100)
.modifier(DraggableModifier(direction: .vertical))
Text("Vertical")
}
Spacer(minLength: 100)
Rectangle()
.foregroundColor(.red)
.frame(width: 100, height: 100)
.modifier(DraggableModifier(direction: .horizontal))
Text(verbatim: "Horizontal")
Spacer(minLength: 100)
}
}
}
Result:
Related Topics
Flutter iOS Build Failure Error with Multiple Commands After the Xcode Upgrade
iOS 5 Gm: <Error>: More Than Maximum 5 Filtered Album Lists Trying to Register. This Will Fail
Firebase Queryorderedbychild() Method Not Giving Sorted Data
Swipe Left or Right to Load the View Controller with the Collection View Cell Highlight
How to Make Alamofire Download Progress Run in Background iOS
Why Does "Try Catch" in Objective-C Cause Memory Leak
Why Isn't Preferredcontentsize Used by iPhone 6 Plus Landscape
iOS How to Dismiss Uialertview with One Tap Anywhere
Launchd_Sim Crashing: Could Not Create Temporary State Directory
Swift Execute Asynchronous Tasks in Order
React-Native Loading Image Over Https Works While Http Does Not Work
403 Error: Disallowed_Useragent
How to Create a PDF File Programmatically in an iOS Application
Firebase - How to Get the Key Value in Observeeventtype = Value
Possible to Send Automated Email