SwiftUI Custom Slider with max Value - swift

I use this great code to get a slider. here
but how can i set the max value to 30 not to 100?
this example is from 0 to 100.
hope everyone can help.
struct CustomView: View {
#Binding var percentage: Float // or some value binded
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.gray)
Rectangle()
.foregroundColor(.accentColor)
.frame(width: geometry.size.width * CGFloat(self.percentage / 100))
}
.cornerRadius(12)
.gesture(DragGesture(minimumDistance: 0)
.onChanged({ value in
self.percentage = min(max(0, Float(value.location.x / geometry.size.width * 100)), 100)
}))
}
}
}

You just need to replace the 100 with 30 to get bound from 0 to 30
struct CustomView: View {
#Binding var percentage: Float // or some value binded
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.gray)
Rectangle()
.foregroundColor(.accentColor)
.frame(width: geometry.size.width * CGFloat(self.percentage / 30))
}
.cornerRadius(12)
.gesture(DragGesture(minimumDistance: 0)
.onChanged({ value in
self.percentage = min(max(0, Float(value.location.x / geometry.size.width * 30)), 30)
print(self.percentage)
}))
}
}
}

Related

SwiftUI App crashes after second continuous open

I have a working SwiftUI app with a start animation that appears to sometimes cause it to crash - this happens only when the user quits the app completely right before the animation reaches its endpoint and then immediately reopens the app - it skips the launch screen, goes straight to the main one, and then crashes. I have put my code below - thank you for your help.
import SwiftUI
import PDFKit
struct ContentView: View {
#Environment(\.presentationMode) var presentationMode
#Environment(\.scenePhase) var scenePhase
#State var inside = false
#AppStorage("ShowLoadingScreen") var showLoadingScreen = UserDefaults.standard.value(forKey: "ShowingLoadingScreen") as? Bool ?? true
var body: some View {
NavigationView {
ZStack {
Rectangle()
.ignoresSafeArea()
.foregroundColor(Color("LightBlue"))
.navigationTitle("Home")
.navigationBarHidden(true)
VStack {
if inside || !showLoadingScreen{
Spacer()
Text("Bugle") .foregroundColor(Color("DarkBlue"))
.font(Font.custom("Copperplate", size: UIScreen.main.bounds.height * 0.1)) .padding(.top, UIScreen.main.bounds.height * 0.015)
.multilineTextAlignment(.center)
.padding()
Spacer()
.navigationBarHidden(true)
NavigationLink {
MonthlyBugleView()
.navigationTitle("This Month's Bugle")
} label: {
ZStack {
RoundedRectangle(cornerRadius: 100)
.frame(width: UIScreen.main.bounds.width * 0.9, height: UIScreen.main.bounds.height * 0.15)
.opacity(0.8)
Text("This Month's Bugle").font(Font.custom("Copperplate", size: UIScreen.main.bounds.height * 0.05)) .padding(.top, UIScreen.main.bounds.height * 0.015)
.foregroundColor(.white)
.padding()
}
}
Spacer()
NavigationLink {
PDFSwiftUIView(StringToBeLoaded: "SampleLink")
} label: {
ZStack {
RoundedRectangle(cornerRadius: 100)
.frame(width: UIScreen.main.bounds.width * 0.9, height: UIScreen.main.bounds.height * 0.15)
.opacity(0.8)
.foregroundColor(Color("DarkBlue"))
Text("Previous Bugles").font(Font.custom("Copperplate", size: UIScreen.main.bounds.height * 0.05)) .padding(.top, UIScreen.main.bounds.height * 0.015)
.foregroundColor(.white)
.padding()
}
}
Spacer()
NavigationLink {
MoreWinaduStuffView()
.navigationBarHidden(true)
} label: {
ZStack {
RoundedRectangle(cornerRadius: 100)
.frame(width: UIScreen.main.bounds.width * 0.9, height: UIScreen.main.bounds.height * 0.15)
.opacity(0.8)
Text("More Stuff").font(Font.custom("Copperplate", size: UIScreen.main.bounds.height * 0.05)) .padding(.top, UIScreen.main.bounds.height * 0.015)
.foregroundColor(.white)
}
}
Spacer()
} else if showLoadingScreen{
StartAnimationView()
.transition(.opacity)
}
}
.onAppear( perform: {
DispatchQueue.main.asyncAfter(deadline: .now()+3.5){
withAnimation {
inside.toggle()
showLoadingScreen = false
}
}
}
)
}
} .onChange(of: scenePhase) { newPhase in
if newPhase == .inactive {
showLoadingScreen = true
} else if newPhase == .active {
if inside == true{
showLoadingScreen = false
}
} else if newPhase == .background {
showLoadingScreen = true
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and this is my StartAnimationView:
import SwiftUI
struct StartAnimationView: View {
let color: UIColor = UIColor(red: 29/255.0, green: 161/255.0, blue: 242/255.0, alpha: 1)
#State var animate: Bool = false
#State var offset: CGFloat = 0
var body: some View {
VStack{
//Content
ZStack{
Color("LightBlue")
Image("sample start")
.resizable()
.scaledToFit()
.padding()
.offset(x: 0, y: offset)
.foregroundColor(.white)
.frame(width: 150, height: 150, alignment: .center)
.scaleEffect(animate ? UIScreen.main.bounds.height * 0.2 : UIScreen.main.bounds.height * 0.0075)
.animation(.easeIn(duration: 3.5), value: animate)
.animation(.easeIn(duration: 0.1), value: offset)
} .ignoresSafeArea()
}.onAppear{
DispatchQueue.main.asyncAfter(deadline: .now()+0.3){
offset = 8
animate.toggle()
}
}
}
}
struct StartAnimationView_Previews: PreviewProvider {
static var previews: some View {
StartAnimationView()
}
}
Your actions in onChange of newPhase may confuse SwiftUI as you ask SwiftUI to show animation when going to inactive or background.

SwiftUI: Frame modifier in ZStack affecting other views

struct CircleTestView: View {
let diameter: CGFloat = 433
var body: some View {
ZStack {
Color(.yellow)
.ignoresSafeArea()
VStack {
Circle()
.fill(Color(.green))
.frame(width: diameter, height: diameter)
.padding(.top, -(diameter / 2))
Spacer()
}
VStack {
Spacer()
Button {} label: {
Color(.red)
.frame(height: 55)
.padding([.leading, .trailing], 16)
}
}
}
}
}
The code above creates the first image, yet for some reason if I remove the line the sets the frame for the Circle (ie. .frame(width: diameter, height: diameter)) I get the second image.
2.
I want the circle how it is in the first screen, and the button how it is in the second screen, but can't seem to achieve this. Somehow setting the frame of the Circle is affecting the other views, even though they're in a ZStack. Is this a bug with ZStacks, or am I misunderstanding how they work?
Lets call this one approach a:
struct CircleTestView: View {
let diameter: CGFloat = 433
var body: some View {
ZStack {
Color(.yellow)
.ignoresSafeArea()
VStack {
Circle()
.fill(Color(.green))
.frame(width: diameter, height: diameter)
.padding(.top, -(diameter / 2))
Spacer()
}
VStack {
Spacer()
Button {} label: {
Color(.red)
.frame(height: 55)
}
}
.padding(.horizontal, 16)
}
}
}
Lets call this one approach b:
struct CircleTestView: View {
let diameter: CGFloat = 433
var body: some View {
ZStack {
Color(.yellow)
.ignoresSafeArea()
VStack {
Circle()
.fill(Color(.green))
.offset(x: 0, y: -(diameter / 1.00))
// increment/decrement the offset by .01 example:
// .offset(x: 0, y: -(diameter / 1.06))
Spacer()
}
VStack {
Spacer()
Button {} label: {
Color(.red)
.frame(height: 55)
.padding([.leading, .trailing], 16)
}
}
}
}
}
A combination of the two approaches would land you at approach c.
Do any of these achieve what you are looking for?

How do you implement a decimal keyboard?

Commented out in my view struct, I have the keyboard I want to limit the user to when filling out the long/lat text fields. There are other threads that are close to this question, but I do not understand what they are referencing in their answers.
How do you limit the user to the decimal + punctuation keyboard?
//
// ContentView.swift
// Geo Calculator App
// App that calculates distance between two long/lat values
//
// Created on 9/25/21.
//
import SwiftUI
struct ContentView: View {
#State var longitude1 = ""
#State var latitude1 = ""
#State var longitude2 = ""
#State var latitude2 = ""
//self.LongLatTextField.keyboardType = UIKeyboardType.decimalPad
var body: some View {
VStack {
Spacer()
Text("Geo Calculator App")
.font(.system(size: 24, weight: .bold))
.foregroundColor(.black)
Text("Calculate distances between long/lat values")
.font(.system(size: 16, weight: .light))
.foregroundColor(.gray)
HStack {
HStack {
TextField("Longitude A", text: $longitude1)
.foregroundColor(.blue)
}.frame(height: 60)
.padding(.horizontal, 15)
.background(Color.white)
.cornerRadius(9)
HStack {
TextField("Latitude A", text: $latitude1)
.foregroundColor(.blue)
}.frame(height: 60)
.padding(.horizontal, 15)
.background(Color.white)
.cornerRadius(9)
}.frame(height: 60)
.padding(.horizontal, 15)
.cornerRadius(9)
.padding(.horizontal, 5)
HStack {
HStack {
TextField("Longitude B", text: $longitude2)
.foregroundColor(.blue)
}.frame(height: 60)
.padding(.horizontal, 15)
.background(Color.white)
.cornerRadius(9)
HStack {
TextField("Latitude B", text: $latitude2)
.foregroundColor(.blue)
}.frame(height: 60)
.padding(.horizontal, 15)
.background(Color.white)
.cornerRadius(9)
}.frame(height: 60)
.padding(.horizontal, 15)
.cornerRadius(9)
.padding(.horizontal, 5)
//Put label here to display output on button click
Spacer()
Button(action: {}) {
Text("Calculate")
.foregroundColor(.white)
.font(.system(size: 24, weight: .medium))
}.frame(maxWidth: .infinity)
.padding(.vertical, 15)
.background(Color.green.opacity(10))
.cornerRadius(30)
.padding(.horizontal, 70)
Spacer()
}.background(Color.white)
.edgesIgnoringSafeArea(.all)
}
}
//------------------------------------------------------
//Distance calculations, sources below
//geodatasource.com/developers/swift
//sisense.com/blog/latitude-longitude-distance-calculation-explained
func degreeToRadian(deg:Double) -> Double {
return deg * Double.pi / 180
}
func radianToDegree(rad:Double) -> Double {
return rad * 180 / Double.pi
}
func distance(lat1:Double, lon1:Double, lat2:Double, lon2:Double) -> Double {
let theta = lon1 - lon2
var dist = sin(degreeToRadian(deg: lat1)) * sin(degreeToRadian(deg: lat2)) + cos(degreeToRadian(deg: lat1)) * cos(degreeToRadian(deg: lat2)) * cos(degreeToRadian(deg: theta))
dist = acos(dist)
dist = radianToDegree(rad: dist)
dist = dist * 60 * 1.1515 * 1.609344
return dist
}
//------------------------------------------------------
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can just use the keyboard modifier on your Textfield:
.keyboardType(.decimalPad)
Documentation: https://developer.apple.com/documentation/uikit/uikeyboardtype

Cannot assign value of type 'Double' to type 'Binding<Double>'

I want the user to pick his height through the picker. And then want to record the user's height in inches or centimeters into the Double variable which in my ContenvtView struct. However, can't assign any value to my #Binding Double. Xcode yelling the thing on the title or says that I can't assign Double to Binding. What am I missing? Here is the code:
struct HeightPicker: View {
#State var foot = 0.0
#State var inch = 0.0
#State var meter = 0.0
#State var cm = 0.0
#State var measurement = ""
#Binding var metric: Bool
#Binding var height: Double
var feet = [Int](0..<10)
var inches = [Int](0..<12)
var meters = [Int](0..<3)
var cms = [Int](0..<100)
var measurements = ["in", "cm" ]
var body: some View{
VStack{
GeometryReader() { geometry in
HStack {
if(metric == false) {
Picker(selection: self.$foot, label: Text("")) {
ForEach(0 ..< self.feet.count){ index in
Text("\(self.feet[index])").tag(self.feet[index])
}
}
.frame(width: geometry.size.width/4,height: geometry.size.height, alignment: .center)
.clipped()
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
Text("\"")
Picker(selection: self.$inch, label: Text("")) {
ForEach(0 ..< self.inches.count){ index in
Text("\(self.inches[index])").tag(self.inches[index])
}
}
.frame(width: geometry.size.width/4,height: geometry.size.height, alignment: .center)
.clipped()
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
var heightInches = (self.foot * 12) + self.inch
Text("'")
self.$height = heightInches
} else if(metric == true) {
Picker(selection: self.$meter, label: Text("")) {
ForEach(0 ..< self.meters.count){ index in
Text("\(self.meters[index])").tag(self.meters[index])
}
}
.frame(width: geometry.size.width/4, height: geometry.size.height, alignment: .center)
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.clipped()
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
Text("m")
Picker(selection: self.$cm, label: Text("")) {
ForEach(0 ..< self.cms.count){ index in
Text("\(self.cms[index])").tag(self.cms[index])
}
}
.frame(width: geometry.size.width/4,height: geometry.size.height, alignment: .center)
.clipped()
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
Text("cm")
}
}
}.frame(height: 45)
}
.frame(height: 45)
.padding(.top,5)
.padding(.bottom, 5)
}
}
}
you cannot just do this in the middle of a view:
var heightInches = (self.foot * 12) + self.inch
self.$height = heightInches
Try this as an example:
struct HeightPicker: View {
#State var foot: Int = 0 // <---
#State var inch: Int = 0 // <---
#State var meter: Int = 0 // <---
#State var cm: Int = 0 // <---
#State var measurement = ""
#Binding var metric: Bool
#Binding var height: Double
var feet = [Int](0..<10)
var inches = [Int](0..<12)
var meters = [Int](0..<3)
var cms = [Int](0..<100)
var measurements = ["in", "cm" ]
var body: some View{
VStack{
GeometryReader() { geometry in
HStack {
if !metric { // <---
Picker(selection: self.$foot, label: Text("")) {
ForEach(0 ..< self.feet.count){ index in
Text("\(self.feet[index])").tag(self.feet[index])
}
}
// add this
.onChange(of: foot) { _ in
self.height = Double(self.foot * 12 + self.inch)
print("--> height: \(height)")
}
.frame(width: geometry.size.width/4,height: geometry.size.height, alignment: .center)
.clipped()
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
Text("\"")
Picker(selection: self.$inch, label: Text("")) {
ForEach(0 ..< self.inches.count){ index in
Text("\(self.inches[index])").tag(self.inches[index])
}
}
// add this
.onChange(of: inch) { _ in
self.height = Double(self.foot * 12 + self.inch)
print("--> height: \(height)")
}
.frame(width: geometry.size.width/4,height: geometry.size.height, alignment: .center)
.clipped()
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
Text("'")
} else {
Picker(selection: self.$meter, label: Text("")) {
ForEach(0 ..< self.meters.count){ index in
Text("\(self.meters[index])").tag(self.meters[index])
}
}
.frame(width: geometry.size.width/4, height: geometry.size.height, alignment: .center)
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.clipped()
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
Text("m")
Picker(selection: self.$cm, label: Text("")) {
ForEach(0 ..< self.cms.count){ index in
Text("\(self.cms[index])").tag(self.cms[index])
}
}
.frame(width: geometry.size.width/4,height: geometry.size.height, alignment: .center)
.clipped()
.scaleEffect(CGSize(width: 1.0, height: 1.0))
.pickerStyle(WheelPickerStyle())
.scaledToFit()
.background(Color.white)
Text("cm")
}
}
}.frame(height: 45)
}
.frame(height: 45)
.padding(.top,5)
.padding(.bottom, 5)
}
}

I'd like to use the navigation link.Newbie Wang is in the process of hair loss

I want to use Navigationlink. I've been a novice for 2 weeks since I started.I am currently learning SwiftUi.
I created "OnboredView" after watching YouTube, but I don't know how to connect "OnboredView" to "CountentView".
NavigationView(){
NavigationLink(destination: OnboardView())
I learned how to make it like this through YouTube, but I don't know what to do now. I put it here and there, but the red errors bother me.
Tell me how to connect "NavigationLink" by pressing the button on "CountentView".
I'd like to click "Chevron.Light" to move on to "OnboredView."And if possible, please let me know how I can get rid of the "onboard screen" on the second run?
I am not good at English.I'm sorry. I'm experiencing hair loss again.
import SwiftUI
struct ContentView: View {
#State private var animate: Bool = false
var body: some View {
ZStack{
ZStack{
Image("rogo1")
.resizable()
.frame(width: 75, height: 75)
.offset(y: animate ? -100 : 0)
}
ZStack{
Image("rogo2")
.resizable()
.frame(width: 75, height: 75)
.offset(y: animate ? -100 : 0)
}
VStack {
HStack {
Spacer()
Image("images (1)")
.resizable()
.frame(width: 300, height: 300)
.offset(x: animate ? 300 : 150, y: animate ? -300 : -150)
}
Spacer()
HStack {
Image("images (1)")
.resizable()
.frame(width: 400, height: 400)
.offset(x: animate ? -500 : -150, y: animate ? 500 : 150)
Spacer()
}
}
ZStack(alignment: .bottom){
GeometryReader { g in
VStack (alignment: .leading, spacing: 20){
Text("안녕하세요!")
.font(.title)
.fontWeight(.semibold)
.padding(.top, 20)
//인삿말과 회원가입
Text("기분 좋은 매일습관을 만들기 위한 앱 ( ) 입니다! 시간표와 더불어 루틴을 함께 할수
있도록 설계 되었습니다.저희 ( )와 함께 계획해봐요!")
.fontWeight(.medium)
.multilineTextAlignment(.center)//중앙으로 결집
.padding(5)
ZStack {
Button(action: {},label: {
Image(systemName: "chevron.right")
.font(.system(size:20, weight: .semibold))
.frame(width: 60, height: 60)
.foregroundColor(.black)
.background(Color.white)
.clipShape(Circle())
.overlay(
ZStack {
Circle()
.stroke(Color.black.opacity(0.04),lineWidth: 4)
Circle()
.trim(from: 0, to: 0.03)
.stroke(Color.white,lineWidth: 4)
.rotationEffect(.init(degrees: -40))
})
})
.padding(-10)
}
Spacer()
}
.frame(maxWidth: .infinity)
.padding(.horizontal, 30)
.background(Color.green)
.clipShape(CustomShape(leftCorner: .topLeft, rightCorner: .topRight,
radii: 20))
.offset(y: animate ? g.size.height : UIScreen.main.bounds.height)
}
}.frame(height: 275)
//여기까지 짤라도 됨 온보드
}
.frame(maxWidth: .infinity)
.edgesIgnoringSafeArea(.all)
.onAppear(perform: {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
withAnimation(Animation.easeOut(duration: 0.45)){
animate.toggle()
}
}
})
}
{
struct CustomShape: Shape {
var leftCorner: UIRectCorner
var rightCorner: UIRectCorner
var radii: CGFloat
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners:
[leftCorner,rightCorner], cornerRadii: CGSize(width: radii, height: radii))
return Path(path.cgPath)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
Group {
}
}
}
}
import SwiftUI
struct OnboardView: View {
#AppStorage("currentPage") var currentPage = 1
var body: some View {
if currentPage > totalPages {
Home()
}else{
WalkthroughScreen()
}
}
}
struct OnboardView_Previews: PreviewProvider {
static var previews: some View {
OnboardView()
}
}
struct Home: View {
var body: some View{
Text("welcome To Home!!!")
.font(.title)
.fontWeight(.heavy)
}
}
//..Walkthrough Screen..
struct WalkthroughScreen: View {
#AppStorage("currentPage") var currentPage = 1
var body: some View {
//For Slide Animation
ZStack{
//Changing Between Views..
if currentPage == 1 {
ScreenView(image: "image1", title: "Step1", detail: "", bgColor:
Color(.white))
//transition(.scale)영상에서는 넣었으나 오류가나서 사용하지 못함
}
if currentPage == 2 {
ScreenView(image: "image2", title: "Step2", detail: "", bgColor:
Color(.white))
}
if currentPage == 3 {
ScreenView(image: "image3", title: "Step3", detail: "아니 ㅡㅡ 이런 방법이 유레카",
bgColor: Color(.white))
}
}
.overlay(
Button(action: {
//changing views
withAnimation(.easeInOut){
if currentPage < totalPages {
currentPage += 1
}else{
currentPage = 1
//For app testing ONly
}
}
}, label: {
Image(systemName: "chevron.right")
.font(.system(size: 20, weight: .semibold))
.foregroundColor(.black)
.frame(width: 60, height: 60)
.clipShape(Circle())
//strclulat Slider
.overlay(
ZStack{
Circle()
.stroke(Color.black.opacity(0.04),lineWidth: 4
Circle()
.trim(from: 0, to: CGFloat(currentPage) /
CGFloat(totalPages))
.stroke(Color.green,lineWidth: 4)
.rotationEffect(.init(degrees: -99))
}
.padding(-15)
)
})
.padding(.bottom,20)
,alignment: .bottom
)
}
}
struct ScreenView: View {
var image: String
var title: String
var detail: String
var bgColor: Color
#AppStorage("currentPage") var currentPage = 1
var body: some View {
VStack(spacing:20){
HStack {
//Showing it only for first page..
if currentPage == 1{
Text("Hello Members!")
.font(.title)
.fontWeight(.semibold)
//Letter Spacing
.kerning(1.4)
}else{
//Back Butten..
Button(action: {
withAnimation(.easeInOut){
currentPage -= 1
}
}, label: {
Image(systemName: "chevron.left")
.foregroundColor(.white)
.padding(.vertical,10)
.padding(.horizontal)
.background(Color.black.opacity(0.4))
.cornerRadius(10)
})
}
Spacer()
Button(action: {
withAnimation(.easeInOut){
currentPage = 4
}
}, label: {
Text("Skip")//글자입력
.fontWeight(.semibold)//글자 폰트변경
.kerning(1.2)//글자간 간격 조정
})
}
.foregroundColor(.black)//그라운드 컬러 변경
.padding()
Spacer(minLength: 0)//수평,수직 줄바꿈
Image(image)//이미지 삽입
.resizable()//크기 확대
.aspectRatio(contentMode: .fit)//이미지 크기
Text(title)
.font(.title)//폰트 크기변경
.fontWeight(.bold)//폰트 두께 변경
.foregroundColor(.black)//색깔 변경
.padding(.top)
//Change with your Own Thing..
Text(detail)
.fontWeight(.semibold)
.kerning(1.3)//자간조정
.multilineTextAlignment(.center)//텍스트를 중앙으로 결집
Spacer(minLength: 220)//minimun Spacing When phone is reducing수직위치 조정
}
.background(bgColor.cornerRadius(10).ignoresSafeArea())
}
}
var totalPages = 3