How can I do a MatchedGeometryEffect animation? - swift

Do you have any approach about how to scale ( with MatchedGeometryEffect ) my 'MealTabView' ( the cells that you can see in the picture ) so that it can fill an entire view ? :
Here's the source code for the 'cells' that you can see :
struct MealTabView:View{
var meal: Meal
#Binding var selectedBtn: [Int]
var body: some View{
ZStack{
Button(action: {
if selectedBtn.contains(self.meal.id){
selectedBtn.remove(at: selectedBtn.firstIndex(of: self.meal.id)!)
} else{
selectedBtn.append(self.meal.id)
}
}, label: {
ZStack(alignment:.center){
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.white)
.frame(width: 150, height: 200)
.shadow(color: .black.opacity(0.1), radius: 5)
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(selectedBtn.contains(self.meal.id) ? Color.black : Color.white, lineWidth: 4)
)
VStack(alignment:.center){
switch meal.id { // for the future images
default:
Image("meal")
.resizable()
.frame(width: 100, height: 100)
.padding(.top,30)
}
Spacer()
Text(meal.title)
.padding(.bottom,20)
.wotfard(size: 20) // custom modifier for a special font
.foregroundColor(.black)
}
}.frame(width: 150, height: 200)
})
}
}
}

Related

Why does ScrollView randomly push my views content downwards?

Here is my code:
import SwiftUI
struct TrendingView : View {
#State var selectedTab: Tabs = .hot
#State private var searchText = ""
var body: some View {
NavigationView {
VStack {
HStack {
Text(" Trending")
.font(.largeTitle)
.frame(width: 175, height: 50, alignment: .leading)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 2)
)
.italic()
.fontWeight(.heavy)
.foregroundColor(Color(hue: 1.0, saturation: 0.977, brightness: 0.985))
.offset(x: -75, y: -25)
.padding(.bottom, -20)
NavigationLink(destination: testT()) {
Image(systemName: "magnifyingglass.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
.foregroundColor(.black)
.padding(.bottom, -20)
}
.offset(x: 50, y: -25)
.padding(.leading, 5.0)
}
ScrollView {
VStack {
Text("Hot Items ๐Ÿ”ฅ")
.bold()
.italic()
.offset(x: 5)
.frame(width: 120, height: 25, alignment: .leading)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 2)
)
.background(.yellow)
.offset(x: -130, y: 5)
ScrollView(.horizontal) {
HStack {
NavigationLink(destination: ImageTest()) {
Image("testpic1")
.resizable()
.frame(width: 200, height: 200)
}
NavigationLink(destination: ImageTest()) {
Image("testpic3")
.resizable()
.frame(width: 200, height: 200)
}
}.offset(y: 30)
}
}
}
There seems to be some error with scrollview above my vstack as it randomly causes all the content on this view to shift downwards when switching to and from other views.Ive tried removing the scrollview and the error disappears and also tried changing it around in other places but I still get the error Can anybody point me in the right direction? Thanks
Fixed, added to the bottom of my code and it seems like the issue is solved
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)

Swift UI animation diagonal instead horizontal

I'm trying to make a loading view with some animations, but instead of the RoundRectangle offset it self horizontaly as it's defined in the code, it is actually moving in diagonal.
Why is it animating in diagonal?
Here's my struct:
struct LoadingIndicator: View {
let textToDisplay:String
#State private var isLoading:Bool = false
var body: some View {
ZStack {
Text(textToDisplay)
.fontWeight(.bold)
.offset(x: 0, y: -25)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.gray, lineWidth: 3)
.frame(width: 250, height: 3)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.indigo, lineWidth: 3)
.frame(width: 30, height: 3)
.offset(x: (isLoading ? 110 : -110), y: 0)
.animation(.linear(duration: 1).repeatForever(), value: isLoading)
}.onAppear {
self.isLoading = true
}
}
}
This is what I got:
This is what I wanted to achieve:
After seing this question: SwiftUI: Broken explicit animations in NavigationView?
I solved my problem:
struct LoadingIndicator: View {
let textToDisplay:String
#State private var isLoading:Bool = false
var body: some View {
ZStack {
Text(textToDisplay)
.fontWeight(.bold)
.offset(x: 0, y: -25)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.gray, lineWidth: 3)
.frame(width: 250, height: 3)
RoundedRectangle(cornerRadius: 3)
.stroke(Color.indigo, lineWidth: 3)
.frame(width: 30, height: 3)
.offset(x: (isLoading ? 110 : -110), y: 0)
.animation(.linear(duration: 1).repeatForever(), value: isLoading)
}.onAppear {
DispatchQueue.main.async {
self.isLoading = true
}
}
}
}

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

SwiftUI unable to align button with topLeading

I have a button which I am unable to align to the top left with topLeading.
What I hope to accomplish:
my code:
var body: some View {
ZStack {
ZStack(alignment: .topLeading) {
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "list.dash")
.foregroundColor(.black)
}
.padding(.trailing, 20)
.frame(width: 90, height: 60)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color("buttonShadow"), radius: 10, x: 0, y: 10)
}
Spacer()
}
}
}
But I get this:
The issue is that your wrapping views (two ZStacks) by default will take up only the space of their content, which is only the button. If you add a frame to allow the wrapper to expand you'll see the alignment working as you expect.
var body: some View {
ZStack {
ZStack {
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "list.dash")
.foregroundColor(.black)
}
.padding(.trailing, 20)
.frame(width: 90, height: 60)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color.gray, radius: 10, x: 0, y: 10)
}
Spacer()
}
.frame(
maxWidth: .infinity,
maxHeight: .infinity,
alignment: Alignment.topLeading
)
}
}
Add background space consumer, like below (tested with Xcode 11.4)
ZStack(alignment: .topLeading) {
Color.clear // << here !!
Button(action: { self.show.toggle() }) {