Custom range slider in SwiftUI - ios

Trying to add slider minimum & maximum range selection with value in SwiftUI.
Add min & max value like above image.
// define min & max value
#State var minValue: Float = 0.0
#State var maxValue: Float = Float(UIScreen.main.bounds.width - 50.0)
// setup slider view
ZStack (alignment: Alignment(horizontal: .leading, vertical: .center), content: {
Rectangle()
.fill(Color(UIColor.systemTeal).opacity(0.3))
.cornerRadius(30)
.frame(width: CGFloat(self.max), height: 30)
Rectangle()
.fill(Color.blue.opacity(25))
.cornerRadius(30)
.offset(x: CGFloat(self.min))
.frame(width: CGFloat((self.max + 20) - self.min), height: 30)
Circle()
.fill(Color.orange)
.frame(width: 30, height: 30)
.offset(x: CGFloat(self.min))
.gesture(DragGesture().onChanged({ (value) in
if value.location.x > 8 && value.location.x <= self.sliderWidth &&
value.location.x < CGFloat(self.max) {
self.min = Double(value.location.x)
}
}))
Circle()
.fill(Color.orange)
.frame(width: 30, height: 30)
.offset(x: CGFloat(self.max))
.gesture(DragGesture().onChanged({ (value) in
if value.location.x <= self.sliderWidth && value.location.x > CGFloat(self.min) {
self.max = Double(value.location.x)
}
}))
})
.padding()
In this code, I'm facing issue with max value, while drag the max value and then changed the slider track width, so it's not moving perfectly. I tried with different different frame, but it doesn't work.

Finally, I got the solution.
// define min & max value
#State var minValue: Float = 0.0
#State var maxValue: Float = Float(UIScreen.main.bounds.width - 50.0)
// setup slider view
VStack {
HStack {
Text("0")
.offset(x: 28, y: 20)
.frame(width: 30, height: 30, alignment: .leading)
.foregroundColor(Color.black)
Spacer()
Text("100")
.offset(x: -18, y: 20)
.frame(width: 30, height: 30, alignment: .trailing)
.foregroundColor(Color.black)
}
ZStack(alignment: Alignment(horizontal: .leading, vertical: .center), content: {
Capsule()
.fill(Color.black.opacity(25))
.frame(width: CGFloat((UIScreen.main.bounds.width - 50) + 10), height: 30)
Capsule()
.fill(Color.black.opacity(25))
.offset(x: CGFloat(self.minValue))
.frame(width: CGFloat((self.maxValue) - self.minValue), height: 30)
Circle()
.fill(Color.orange)
.frame(width: 30, height: 30)
.background(Circle().stroke(Color.white, lineWidth: 5))
.offset(x: CGFloat(self.minValue))
.gesture(DragGesture().onChanged({ (value) in
if value.location.x > 8 && value.location.x <= (UIScreen.main.bounds.width - 50) &&
value.location.x < CGFloat(self.maxValue - 30) {
self.minValue = Float(value.location.x - 8)
}
}))
Text(String(format: "%.0f", (CGFloat(self.minValue) / (UIScreen.main.bounds.width - 50)) * 100))
.offset(x: CGFloat(self.minValue))
.frame(width: 30, height: 30, alignment: .center)
.foregroundColor(Color.black)
Circle()
.fill(Color.orange)
.frame(width: 30, height: 30)
.background(Circle().stroke(Color.white, lineWidth: 5))
.offset(x: CGFloat(self.maxValue - 18))
.gesture(DragGesture().onChanged({ (value) in
if value.location.x - 8 <= (UIScreen.main.bounds.width - 50) && value.location.x > CGFloat(self.minValue + 50) {
self.maxValue = Float(value.location.x - 8)
}
}))
Text(String(format: "%.0f", (CGFloat(self.maxValue) / (UIScreen.main.bounds.width - 50)) * 100))
.offset(x: CGFloat(self.maxValue - 18))
.frame(width: 30, height: 30, alignment: .center)
.foregroundColor(Color.black)
})
.padding()
}

Related

Why is there no back button in my view with using NavigationLink?

Below is my code which has 4 VStacks which I want to be clickable to take me to my next view:
ForEach (data) { order in
NavigationLink(destination: OrderDetailView(typeof: order.title)){
VStack(spacing: 32){
HStack {
Text(order.title)
.font(.system(size: 22, weight: .bold))
.foregroundColor(.black)
Spacer(minLength: 0)
}
ZStack {
Circle()
.trim(from: 0, to:1)
.stroke(order.color.opacity(0.05),lineWidth: 10)
.frame(width: ((UIScreen.main.bounds.width - 150) / 2), height: ((UIScreen.main.bounds.width - 150) / 2))
Circle()
.trim(from: 0, to: (order.totalcount/tcount))
.stroke(order.color, style: StrokeStyle(lineWidth: 10, lineCap: .round))
.frame(width: ((UIScreen.main.bounds.width - 150) / 2), height: ((UIScreen.main.bounds.width - 150) / 2))
Text(getPercent(current: order.totalcount, max: tcount) + " %")
.font(.system(size: 22, weight: .bold))
.foregroundColor(order.color)
.rotationEffect(.init(degrees: 90))
}
.rotationEffect(.init(degrees: -90))
Text("Total count: " + getDec(val: order.totalcount))
.font(.system(size: 22, weight: .bold))
.foregroundColor(.black)
}
.padding()
.background(.white)
.cornerRadius(15)
.shadow(color: Color.white.opacity(0.2), radius: 10, x:0, y: 0)
}
Everything works fine and the transition takes place but there is no back button for me to come back to my original view. NavigationLink works fine in other parts of the code but not here, not sure why.

Drawing parallel rectangles in SwiftUI to create a Calendar Icon

I'm trying to create a cardView that has a Date on it. Right now, I have the below look, with today's Date within a Square.
I would like to add 2 parallel lines (shown as RED coloured Lines in below pic) such that it looks like a Calendar Icon. Is it possible to do it using Rectangle() instead of using path()
Q: How do I draw the 2 lines thru the top, centre of the Square?
I've tried permutations of this
Rectangle().frame(width: 3, height: 5, alignment: .top).opacity(0.9)
but obviously, it didn't work as the line ended up in the middle of the Date.
VStack {
Text(FormatDisplay.yyyymmdd_day0(inputDate: workout.date))
.padding(2)
.background(RoundedRectangle(cornerRadius: 2).stroke(Color(UIColor.label).opacity(0.8), lineWidth: 2))
.font(.footnote)
.minimumScaleFactor(0.5)
if workout.icuId != "" {
Image("intervalsLogo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20, alignment: .top)
.clipShape(Circle())
}
if workout.priorityCompliance > 0 {
RingView(compliancePct: workout.priorityCompliance, lineWidth: 2, primaryColor: Color.orange, secondaryColor: Color.orange, size: .tiny)
.frame(width: 20, height: 20)
.font(.caption)
}
Spacer()
}
}
This is what the end result I'm looking for. (the horizontal line above the number is an added bonus if possible)
I have bit modified your code. get This if useful.
RoundedRectangle(cornerRadius: 3)
.stroke(Color(UIColor.label).opacity(0.8), lineWidth: 3)
.frame(width: 35, height: 35, alignment: .center)
.overlay(
VStack(spacing: 2.5) {
HStack(spacing: 8) {
ForEach(0..<3) { _ in
RoundedRectangle(cornerRadius: 3)
.stroke(Color.black.opacity(0.8), lineWidth: 1.5)
.frame(width: 1, height: 7, alignment: .top)
}
}
.offset(y: -3.5)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.black.opacity(0.8), lineWidth: 1.5)
.frame(height: 1)
.padding(.horizontal, 5)
Text(date)
.font(.system(size: 15))
.fontWeight(.bold)
}
,alignment: .top
)
preview
So yeah.. couple hours after I posted this.. figured out the solution.
Key was using the .offset(x: y:) modifier
VStack {
ZStack {
Text(FormatDisplay.yyyymmdd_day0(inputDate: workout.date))
.offset(y: 1)
RoundedRectangle(cornerRadius: 2)
.stroke(Color(UIColor.label).opacity(0.8), lineWidth: 2)
.frame(width: 20, height: 20, alignment: .center)
.offset(y: 0)
RoundedRectangle(cornerRadius: 2)
.stroke(Color.black.opacity(0.8), lineWidth: 2)
.frame(width: 1, height: 6, alignment: .top)
.offset(x: 4, y: -10)
RoundedRectangle(cornerRadius: 2)
.stroke(Color.black.opacity(0.8), lineWidth: 2)
.frame(width: 1, height: 6, alignment: .top)
.offset(x: -4, y: -10)
}

Rotation issue in ZStack - SwiftUI

Here , I'm trying to apply same rotation in both these elements.But these object or elements are doing un wanted behaviour, they are not rotating as same as others . So I want to rotate them all as same , So that no the blue card gets behind the image.
I mean I want them all to be in same rotation.
Here my code -
var body: some View {
VStack(spacing : 10){
ZStack {
ZStack (alignment : .bottom){
ZStack(alignment : .bottom){
Image("cat")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: length, height: length)
Text("<>")
.frame(width: 20, height: 20)
.padding()
.background(Color.orange)
.cornerRadius(15)
}
.frame(width: length, height: length)
.background(.blue)
.rotationEffect(Angle(degrees: Double(-totalRotation.width)))
Text("<>")
.frame(width: 20, height: 20)
.padding()
.background(Color.orange)
.cornerRadius(15)
.gesture(
DragGesture()
.onChanged { value in
totalRotation.width = value.translation.width + currentRotation.width
}
.onEnded { value in
currentRotation = totalRotation
mainAngle = totalRotation
}
)
Text("\(mainAngle.width) --- \(totalRotation.width)")
.foregroundColor(.white)
}
.frame(width: length, height: length)
.background(.blue)
.rotationEffect(Angle(degrees: Double(-mainAngle.width)))
}
.frame(width: length, height: length)
.rotationEffect(Angle(degrees: Double(-mainAngle.width)))
}
.frame(width: length, height: length)
Text("\(angle)")
}
}[![Here the image to get much information][1]][1]

How to shape an Image into an circle

iam unsing a UiKit Image picker and trying to setup an Profil Pic for the User. Everything's works fine, the image gets displayed. But iam trying to make it into a Circle. I Tried using clipShape with an circle but it doesn't work. i followed the tutorial "My Images 1: Photo Picker and Camera in SwiftUI"
struct ProfileView: View {
#EnvironmentObject var appUser: User
#EnvironmentObject var appInfo: AppInformation
var body: some View {
ZStack {
Rectangle()
.frame(width: 400, height: 720)
.cornerRadius(50)
.foregroundColor(.gray)
.overlay(
HStack {
if let image = appUser.profilBild {
Image(uiImage: image)
.resizable()
.frame(width: 100, height: 100)
.scaledToFill()
.clipShape(RoundedRectangle(cornerRadius: 15))
.overlay(Circle().stroke(Color.orange, lineWidth: 10))
.onTapGesture {
appInfo.source = .library
appInfo.showPicker = true
}
.padding(20)
.shadow(radius: 10)
.overlay(
ZStack{
Rectangle()
.frame(width: 20, height: 20)
.offset(x: 35, y: -35)
.foregroundColor(.white)
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 30, height: 30)
.offset(x: 35, y: -35)
.foregroundColor(.blue)
.shadow(radius: 10)
}
)
}
else {
Image(systemName: "person.circle")
.resizable()
.frame(width: 100, height: 100)
.onTapGesture {
appInfo.source = .library
appInfo.showPicker = true
}
.padding(20)
.shadow(radius: 10)
.overlay(
ZStack{
Rectangle()
.frame(width: 20, height: 20)
.offset(x: 35, y: -35)
.foregroundColor(.white)
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 30, height: 30)
.offset(x: 35, y: -35)
.foregroundColor(.blue)
.shadow(radius: 10)
}
)
}
VStack {
Text(appUser.username)
.font(.largeTitle)
.frame(width: 240 ,alignment: .leading)
.offset(x: -10, y: -25)
.lineLimit(1)
Text(appUser.name)
.frame(width: 220, alignment: .leading)
.offset(x: -15,y: -20)
.lineLimit(1)
}
}
.frame(width: 400, height: 720, alignment: .topLeading)
)
.padding()
ZStack {
Rectangle()
.foregroundColor(.secondary)
.frame(width: 380, height: 510)
.cornerRadius(45)
}
.frame(width: 400, height: 700, alignment: .bottom)
.padding()
}
.sheet(isPresented: $appInfo.showPicker) {
ImagePicker(sourceType: appInfo.source == .library ? .photoLibrary : .camera, selectedImage: $appUser.profilBild)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
}
}
You were almost there :)
Your only mistake was the order you placed your modifiers. ORDER MATTERS!!
Place scaledToFill() and clipShape() before the frame modifier.
Like such:
.resizable()
.scaledToFill()
.clipShape(Circle())
.frame(width: size, height: size)

Animated dots relocate after device rotation

I've created a button with three animating dots using SwiftUI. When the screen geometry changes somehow (device rotation, keyboard open, etc.) the animation gets messed up. See the attached video. How can I get the circles to stay in their position relative to the button?
This is my code:
struct ContentView: View {
var body: some View {
Button(action: {}, label: {
AnimatedDot().frame(minWidth: 200, maxWidth: .infinity, minHeight: 20, maxHeight: 20)
}).foregroundColor(Color.blue)
.padding()
.background(Color.gray)
.cornerRadius(10.0)
.frame(minWidth: 0, maxWidth: 350)
}
}
struct AnimatedDot: View {
#State private var shouldAnimate = false
var body: some View {
HStack {
Circle()
.fill(Color.white)
.frame(width: 15, height: 15)
.scaleEffect(shouldAnimate ? 1.0 : 0.5)
.animation(Animation.easeInOut(duration: 0.3).repeatForever())
Circle()
.fill(Color.white)
.frame(width: 15, height: 15)
.scaleEffect(shouldAnimate ? 1.0 : 0.5)
.animation(Animation.easeInOut(duration: 0.3).repeatForever().delay(0.3))
Circle()
.fill(Color.white)
.frame(width: 15, height: 15)
.scaleEffect(shouldAnimate ? 1.0 : 0.5)
.animation(Animation.easeInOut(duration: 0.3).repeatForever().delay(0.6))
}
.onAppear {
self.shouldAnimate = true
}
}
}
Here is fixed body - join animation to state value.
Tested with Xcode 12.1 / iOS 14.1
var body: some View {
HStack {
Circle()
.fill(Color.white)
.frame(width: 15, height: 15)
.scaleEffect(shouldAnimate ? 1.0 : 0.5)
.animation(Animation.easeInOut(duration: 0.3).repeatForever(), value: shouldAnimate)
Circle()
.fill(Color.white)
.frame(width: 15, height: 15)
.scaleEffect(shouldAnimate ? 1.0 : 0.5)
.animation(Animation.easeInOut(duration: 0.3).repeatForever().delay(0.3), value: shouldAnimate)
Circle()
.fill(Color.white)
.frame(width: 15, height: 15)
.scaleEffect(shouldAnimate ? 1.0 : 0.5)
.animation(Animation.easeInOut(duration: 0.3).repeatForever().delay(0.6), value: shouldAnimate)
}
.onAppear {
self.shouldAnimate = true
}
}

Resources