How to make a button draggable/movable with SwiftUI?
Here is a demo of possible approach.
Update: re-tested with Xcode 13.3 / iOS 15.4
See also notes inline.
struct CircleButton: View {
@State private var dragAmount: CGPoint?
var body: some View {
GeometryReader { gp in // just to center initial position
ZStack {
Button(action: self.performAction) {
ZStack {
Circle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
Text("Move me")
.foregroundColor(.white)
.font(.system(.caption, design: .serif))
}
}
// Use .none animation for glue effect
.animation(.default, value: dragAmount)
.position(self.dragAmount ?? CGPoint(x: gp.size.width / 2, y: gp.size.height / 2))
.highPriorityGesture( // << to do no action on drag !!
DragGesture()
.onChanged { self.dragAmount = $0.location})
}
.frame(maxWidth: .infinity, maxHeight: .infinity) // full space
}
}
func performAction() {
print("button pressed")
}
}
I try to create a Button with Drag Gesture like the bubble messenger of android
This is nice!
The .position
expands the view (and the button) to maximum size, that's why you can click everywhere.
The easiest workaround is not using a Button, but making the image itself tappable – see code below.
PS: You don't have to use @GestureState
if you manage the dragging by yourself with onChanged
and onEnded
– you did a double job. I commented out everything you don't need ;)
struct ContentView: View {
private var bround = UIScreen.main.bounds
@State var isShow = false
@State private var location = CGPoint(x: 60, y: 60)
// @GestureState private var startLocation: CGPoint? = nil
var simpleDrag: some Gesture {
DragGesture()
.onChanged { value in
// var newLocation = location
// newLocation.x += value.translation.width
// newLocation.y += value.translation.height
// self.location = newLocation
self.location = value.location
}
.onEnded{ value in
if(value.translation.width > bround.size.width/2) {
self.location.x = bround.size.width - 30
}
else {
self.location.x = 30
}
}
// .updating($startLocation) { (value, startLocation, transaction) in
// startLocation = startLocation ?? location
// }
}
var body: some View {
Image(systemName: "bubble.right.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50)
.animation(.easeInOut, value: location)
.position(location)
.gesture(simpleDrag)
.onTapGesture {
self.isShow.toggle()
if(isShow) {
self.location.x = bround.width/2
}
else {
self.location.x = 30
}
}
}
}
SwiftUI: Place and drag object immediately
A possible approach is to handle drag and creation in "area" (background container), while "item" views are just rendered at the place where needed.
Find below a simplified demo (used Xcode 13.2 / iOS 15.2), see also comments in code snapshot.
Note: tap detection in already "existed" item is an exercise for you.
extension CGPoint: Identifiable { // just a helper for demo
public var id: String { "\(x)-\(y)" }
}
struct TapAndDragDemo: View {
@State private var points: [CGPoint] = [] // << persistent
@State private var point: CGPoint? // << current
@GestureState private var dragState: CGSize = CGSize.zero
var body: some View {
Color.clear.overlay( // << area
Group {
ForEach(points) { // << stored `items`
Rectangle()
.frame(width: 24, height: 24)
.position(x: $0.x, y: $0.y)
}
if let curr = point { // << active `item`
Rectangle().fill(Color.red)
.frame(width: 24, height: 24)
.position(x: curr.x, y: curr.y)
}
}
)
.contentShape(Rectangle()) // << make area tappable
.gesture(DragGesture(minimumDistance: 0.0)
.updating($dragState) { drag, state, _ in
state = drag.translation
}
.onChanged {
point = $0.location // track drag current
}
.onEnded {
points.append($0.location) // push to stored
point = nil
}
)
}
}
SwiftUI: Increase tap/drag area for user interaction
Add a .contentShape(Rectangle())
after the frame
.
SwiftUI, create floating button and need can move anywhere like Assistive Touch
According to my understanding of your question, you want a floating button over WKWebview that you can drag anywhere you want.
Initially, I tried with Button
but there was a conflict with the tap gesture
and drag gesture
. Thus I have used Image for showing icon and added a tap gesture and drag gesture to it.
Below is the code:
import SwiftUI
import WebKit
struct FloatingFab : View {
var body: some View {
ZStack {
WebView(request: URLRequest(url: URL(string: "https://stackoverflow.com/")!))
FloatingView()
}
}
}
struct WebView : UIViewRepresentable {
let request: URLRequest
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.load(request)
}
}
struct FloatingView: View {
@State private var currentPosition: CGSize = .zero
@State private var newPosition: CGSize = .zero
var body: some View {
Image(systemName: "plus.circle.fill")
.resizable()
.foregroundColor(.blue)
.frame(width: 50, height: 50)
.offset(x: self.currentPosition.width, y: self.currentPosition.height)
.onTapGesture(perform: {
debugPrint("Perform you action here")
})
.gesture(DragGesture()
.onChanged { value in
self.currentPosition = CGSize(width: value.translation.width + self.newPosition.width,
height: value.translation.height + self.newPosition.height)
}
.onEnded { value in
self.currentPosition = CGSize(width: value.translation.width + self.newPosition.width,
height: value.translation.height + self.newPosition.height)
self.newPosition = self.currentPosition
}
)
}
}
Related Topics
Uisearchbar's Set_Cancelbuttontext: Ivar Is Prohibited
Closures Return Value (Previously Completionblock)
How to Add Custom Init for String Extension
Need Detailed Explanation for Memoize Implementation in Swift (Wwdc 14, Session 404)
How to Get the Unicode Codepoint Represented by an Integer in Swift
Different Colors for Bars in Barchart Depend on Value
What Does a "Do Statement" Without Catch Block Mean
What Does "Constrain to Margins" Mean in Interface Builder in Xcode 6.0.1
How to Immediately See Swift Errors in Appcode
In What Situation Would One Use Expectationfornotification in Swift Testing
Conform to Protocol and Keep Property Private
Swfitui List Make Scrolling Disabled
Swiftui MACos Commands (Menu Bar) and View
How to Retrieve All Contacts Using Cncontact.Predicateforcontacts
How to Process an Array of Task Asynchronously with Swift Combine
Why Does Type(Of:) Return Metatype, Rather Than T.Type
Safari App Extension Crashes After a Few Seconds for Hello World Project
Struggling with Notificationcenter/Combine in Swiftui/Avplayer