How to restrict drag gesture to particular frame only in swiftUI - ios

I want to drag circle shape only within a frame horizontal. I try below code you can see shape work perfectly horizontal and also left side but when drag right side shape drag up to out of device frame. I check so many answer but did't work anything. also try to add .onEnded but don't work.
please help me
thanks in advance
here is a code that i tried
struct ProgressIndicator: View {
#State private var position = CGSize.zero
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading) {
ZStack {
Circle()
.foregroundColor(Color.blue)
.opacity(0.3)
.frame(width: 40, height: 40, alignment: .leading)
.padding(.bottom, -12)
}
.gesture(
DragGesture()
.onChanged({ (value) in
self.position.width += value.location.x
})
)
.offset(x: self.position.width - 20, y: 0)
}
}
}
}
here is screenshot of circle shape for drag to left and right

You can restrict the position with GeometryReader:
self.position.width = min(self.position.width + value.location.x, geometry.size.width / 2)
Here's the result from Playgrounds:
https://imgur.com/FA5xyAz

Related

How to drag an image behind an image using SwiftUI

hopefully anyone of you can help me and give me advice.
I have the task of making a photo editing application with SwiftUI.
In the application there is a feature to remove the background from a person's photo (so only the object of the person), then I created a new UIImage as a layer to replace the background with an image that can be shifted (behind the photo whose background has been removed), so that it is positioned When I tried to drag the new background but I couldn't, because the photo frame that was removed from the background was blocking the new UIImage background.
this is the code i have made.
struct MaskImage: View {
#State var currentPositions = CGPoint.init(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/4)
#State var currentPosition = CGPoint.init(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/4)
#GestureState private var isDragging = false
var body: some View {
GeometryReader { geo in
HStack {
VStack {
Spacer()
ZStack {
Image("EFFECT-1") // DRAGGABLE PHOTO BACKGROUND
.resizable()
.frame(width: abs(geo.size.width-32), height: 375, alignment: .center)
.position(currentPosition)
.gesture(
DragGesture()
.onChanged { value in
self.currentPosition = value.location
}
.updating($isDragging) { (value, state, transaction) in // 1, 2
state = true
self.currentPosition = value.location
}
)
Image("SEGMENTED_IMAGE_REMOVE_BACKGROUND") // The image that will be the front
.resizable()
.frame(width: abs(geo.size.width - 32), height: abs(round((geo.size.width / 2) * 2.438)), alignment: .center)
}
Spacer()
}
}
.background(Color.red)
}
}
}
You can disable touch interactions for the top image using allowsHitTesting:
Image("SEGMENTED_IMAGE_REMOVE_BACKGROUND") // The image that will be the front
.resizable()
.frame(width: abs(geo.size.width - 32), height: abs(round((geo.size.width / 2) * 2.438)), alignment: .center)
.allowsHitTesting(false)

SwiftUI, Render text off screen that is inside an HStack

I have three views inside an HStack. The first two are HStack's and the third is text. I want to use .offset to make the Text go off screen. I then have a DragGesture() which allows me to pull the view over.
Although when I pull, the text is not there. I played around with the .offset value and when I lowered it so only part of the text is off screen, the rest of the text is not there.
How can I get the text to render/ actually be viewable once I drag the screen?
import SwiftUI
struct ContentView: View {
#State private var draggedOffset = CGSize.zero
var body: some View {
HStack {
Rectangle()
.frame(width: 100, height: 100, alignment: .center)
Spacer()
Rectangle()
.frame(width: 100, height: 100, alignment: .center)
Spacer()
Text("Hello, world! Testing, Testing")
.padding()
.lineLimit(1)
}
.padding()
.animation(.spring())
.offset(x: draggedOffset.width)
.gesture(DragGesture()
.onChanged { value in
self.draggedOffset = value.translation
}
.onEnded { value in
self.draggedOffset = CGSize.zero
}
)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
It's a bit tricky. The main thing here is .fixedSize(horizontal: true, vertical: false) - that lets the Text expand horizontally. There's no need for .lineLimit(1).
You should also remove the Spacer()s. Because those also expand horizontally, SwiftUI will get stuck trying to figure out which ones to expand and which to not.
Then, there's the Color.clear. This has a couple purposes:
Give expected resizing behavior: Color can be freely resized, so if you ever want to use ContentView inside another View, you won't run into trouble.
Align the HStack: Because it's wider than the screen width, SwiftUI will center it by default. You want it aligned left, so you can pass in .leading to the alignment parameter.
struct ContentView: View {
#State private var draggedOffset = CGSize.zero
var body: some View {
Color.clear /// placeholder view that fills the screen width
.background( /// use this to constrain everything to the bounds of the screen
HStack {
Rectangle()
.frame(width: 100, height: 100) /// `alignment` is unnecessary here
Rectangle()
.frame(width: 100, height: 100)
Text("Hello, world! Testing, Testing")
.padding()
.fixedSize(horizontal: true, vertical: false) /// make the Text extend as much as possible horizontally
}, alignment: .leading /// align everything left
)
.padding()
.animation(.spring())
.offset(x: draggedOffset.width)
.gesture(
DragGesture()
.onChanged { value in
self.draggedOffset = value.translation
}
.onEnded { value in
self.draggedOffset = CGSize.zero
}
)
}
}
Result:

How to create SwiftUI HStack that adjusts to accommodate the expansion of one of its subviews?

I have a simple horizontal stack comprising three colour bars:
When the yellow bar width is magnified via a gesture, I would expect the purple bar to move to the right to accommodate the expanded yellow bar but instead the yellow bar simply expands UNDERNEATH the purple bar (which remains in into original position).
struct ContentView: View {
#GestureState var scale = CGFloat(1)
var body: some View {
HStack(spacing: 5) {
Color(.blue)
.frame(width: 100, height: 60, alignment: .leading)
Color(.yellow)
.frame(width: 100, height: 60, alignment: .leading)
.scaleEffect(x: scale, y: 1.0, anchor: .leading)
.gesture(MagnificationGesture()
.updating($scale, body: { (value, scale, trans) in
scale = value
})
)
Color(.purple)
.frame(width: 100, height: 60, alignment: .leading)
}
}
}
How can I achieve dynamic adjustment of the HStack layout whilst one of its components is expanding?
Many thanks...
Robert
you could try this code:
Color(.yellow)
.frame(width: CGFloat(100) * scale, height: 60, alignment: .leading)
.gesture(MagnificationGesture()
.updating($scale, body: { (value, scale, trans) in
scale = value
})
)

Subview have parent's shadow, even with a background

So I just started developing with SwiftUI and I'm running in a small problem. Subviews are also displaying superview's shadow, even if the superview has a background. Does someone know how to fix this?
HStack {
HStack {
[...]
}
.padding(.leading, 12.0)
.padding(.trailing, 4.0)
.padding(.vertical, 16.0)
.background(Color("lightGreen"))
.cornerRadius(10)
}
.padding(8)
.background(Color.white)
.shadow(color: Color("tabShadow"), radius: 0.0, x: 0.0, y: -0.5)
.shadow(color: Color("tabShadow"), radius: 0.0, x: 0.0, y: 0.5)
As stated, the first HStack's shadow shouldn't be replicated into the child one, but it is. Only the first one though. Any hints?
Certain modifiers, when placed on a stack, are inherited by all their children. For instance, if you have a stack containing a bunch of Text views, you can place one .font() modifier on the stack and they will all be modified.
It appears that .shadow() is one of those modifiers. As to why only one is inherited, I suspect that the designers of SwiftUI don't expect .shadow() to be called more than once on a particular view, and didn't test for that.
If you are just trying to get a colored line across the top and bottom of the view, maybe try something like
.background(Color.white)
.background(Color("tabShadow").offset(x: 0, y: -0.5))
.background(Color("tabShadow").offset(x: 0, y: 0.5))
I am also newbie in swiftUI , but I think the problem is related to modifiers order and the fact that they change the View type.
I was able to solve the problem by adding .background(Color.white) and .cornerRadius(2.0) modifiers just before the shadow modifier and that applied the changes in parent (not children) View.
struct TestSwiftUIView: View {
var body: some View {
VStack {
Text("Hello World")
Text("Hello World")
}
.padding()
.background(Color.white)
.cornerRadius(2.0)
.shadow(radius: 3)
}
}
You can try overlay and background tricks when you have to make them render in multiple passes. In above case, the overlay will not be affected by the shadow or other effects.
If you think they are subViews, actually, they just render after the superView. It's a 2D world. So the overlay will be quite independently.
The only problem is just the size of overlay.
The hidden() is used here to occupy the position of invisible overlay. It's very cool if you master these layout skills.
struct ContentView: View {
var body: some View {
HStack {
SubContentView().hidden()
}
.padding(8)
.background(Color.white)
.shadow(color: Color.red, radius: 0, x: 0.0, y: -0.5)
.shadow(color: Color.red, radius: 0, x: 0.0, y: 0.5)
.overlay(SubContentView())
}
}
struct SubContentView: View {
var body: some View {
HStack{
Text("a")
Text("b")
Text("c")
Text("a")
Text("b")
Text("c")
}.padding(.leading, 12.0)
.padding(.trailing, 4.0)
.padding(.vertical, 16.0)
.background(Color.green)
.cornerRadius(10)
}
}
If you want to prevent the shadow the be applied to subviews then use the shadow modifier only inside the .background modifier like so:
VStack {
...
}.background(Color.white.shadow(color: .black.opacity(0.3), radius: 1, x: 1, y: 1))
Just Wrap your main view with some View and setShadow(1) to that view. and setShadow(0) to your main view. it overrides the parents shadow.
Lets say you have:
VStack{
Text("1")
Text("2")
}
and you want to set shadow to this VStack. So, wrap this VStack with another VStack (any View), and setShadow to that stack. Inner shadow overrides outer shadow. Finally your code should be:
VStack{
VStack{
Text("1")
Text("2")
}.shadow(radius: 0)
}
.shadow(radius: 1)

Anchor point in SwiftUI

I'm playing with SwiftUI animations. I wanted to make a square bigger modifying its frame with an animation this way:
struct ContentView: View {
let squareWidth = CGFloat(100)
let squareHeight = CGFloat(100)
#State private var bigger = false
var body: some View {
VStack {
Color.green
.frame(width: bigger ? self.squareWidth * 2 : self.squareWidth, height: self.squareHeight)
.animation(.default)
Button(action: {
self.bigger.toggle()
}) {
Text("Click me!")
}
}
}
}
The animation happens from the centre of the View, that is the anchor point (0.5, 0.5). Is there a way to change this anchor point? I found out that I can use scaleEffect on the view specifying an anchor:
struct ContentView: View {
let squareWidth = CGFloat(100)
let squareHeight = CGFloat(100)
#State private var bigger = false
var body: some View {
VStack {
Color.green
.frame(width: self.squareWidth, height: self.squareHeight)
.scaleEffect(x: bigger ? 2 : 1, y: 1, anchor: .leading)
.animation(.default)
Button(action: {
self.bigger.toggle()
}) {
Text("Click me!")
}
}
}
}
But it doesn't seem exactly the same. If I needed to apply several effects I should specify the anchor point for each of them, instead of specifying the anchor point on the view just one time. On UIKit I could write:
var myView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
myView.layer.anchorPoint = CGPoint(x: 0, y: 0.5) //that is, the .leading of SwiftUI
It is possible, but your approach must change.
There is an implied, built-in centring of views in SwiftUI, which can be confusing.
The parent view is the one placing the content in the centre, recalculating the position of its subviews each time their frames change.
Embedding the VStack in a HStack and adding borders to both clearly shows this behaviour.
Also, note the Spacer(), which pushes the view to the left.
HStack {
VStack {
Color.green
.frame(width: bigger ? self.squareWidth * 2 : self.squareWidth)
.frame(height: self.squareHeight)
.animation(.default)
Button("Click me!") { self.bigger.toggle() }
}.border(Color.black)
Spacer()
}.border(Color.red)
Using the .layer.anchorPoint in UIKit should have the same effect as using scaleEffect/offset/rotationEffect in SwiftUI, as it should affect the origin of the underlying geometry/texture which is being rendered by the GPU.
If you have a complex view (composed of multiple subviews) that needs the same scale effect, you can group them using a Group { } and use .scaleEffect on it.

Resources