SwiftUI - NavigationLink nested within a button - swift

I'm working on a onboarding screen:
VStack {
if #available(iOS 14.0, *) {
TabView(selection: $currentStep) {
ForEach(0..<OnBoardingSteps.count) { it in
VStack {
Text(OnBoardingSteps[it].title)
Image(OnBoardingSteps[it].image).resizable().frame(width: 200, height: 200)
Text(OnBoardingSteps[it].description)
}.tag(it)
}
}.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
} else {
// Fallback on earlier versions
}
HStack {
Spacer()
Spacer()
HStack{
ForEach(0..<OnBoardingSteps.count) { it in
if it == currentStep {
Rectangle()
.frame(width: 10, height: 10)
.cornerRadius(10)
.foregroundColor(.purple)
} else {
Circle()
.frame(width: 10, height: 10)
.foregroundColor(.gray)
}
}
}
Spacer()
Button(action: {
if self.currentStep < OnBoardingSteps.count - 1 {
self.currentStep += 1
} else {
// now when i click 'Done' it should take me to Signup screen
}
}){
HStack {
Text(currentStep < OnBoardingSteps.count - 1 ? "Next" : "Done")
.foregroundColor(.blue)
Image(systemName: currentStep < OnBoardingSteps.count - 1 ? "arrow.right" : "checkmark")
}
}
}
}
In my button I have some logic to change text based on if I'm on the last onBoarding step.
What im trying to achieve is, when im on the last step, when i click 'Done' i should navigate to the Signup screen.

Related

Place group of buttons in top right corner using SwiftUI

So I'm having some issues with SwiftUI and I can't figure out how to properly place an HStack (4 Buttons) in the top right corner, so I wanted to reach out for some help.
Expected Output:
Have the HStack be shown in the top right corner of the screen.
Current Output:
Here is the code that I got:
HStack {
Spacer()
VStack {
// MARK: - CATEGORY BUTTON (MAP PIN IS CLICKED)
// #TODO: FILTERING IS BROKEN, NEEDS TO BE FIXED
Button(action: {
}) //: END Button
// MARK: - FAVORITE BUTTON (MAP PIN IS CLICKED)
// if mainViewModel.selectedItemId != 0 {
Button(action: {
}) //: END Button
// MARK: - GPS BUTTON (MAP PIN IS CLICKED)
// if locationManager.lastLocation != nil {
Button(action: {
}) //: END Button
// MARK: - USER LOCATION BUTTON (MAP PIN IS CLICKED)
Button(action: {
}) //: END Button
// }
// }
} //: END VStack
.background {
RoundedRectangle(cornerRadius: 8)
.fill(Color(uiColor: .systemBackground))
}
.tint(Color(uiColor: .secondaryLabel))
} //: END HStack
.padding()
All help will be highly appreciated!
Here is one way to do it:
Wrap your HStack in another VStack and add a Spacer() View below the HStack to push it up to the top of the screen:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
HStack {
Spacer()
VStack {
Button(action: {
}, label: {
Image(systemName: "globe")
.resizable()
.frame(width: 40, height: 40)
.padding(10)
})
Button(action: {
}, label: {
Image(systemName: "car")
.resizable()
.frame(width: 40, height: 40)
.padding(10)
})
Button(action: {
}, label: {
Image(systemName: "house")
.resizable()
.frame(width: 40, height: 40)
.padding(10)
})
Button(action: {
}, label: {
Image(systemName: "clock")
.resizable()
.frame(width: 40, height: 40)
.padding(10)
})
} //: END VStack
.background {
RoundedRectangle(cornerRadius: 8)
.fill(Color(uiColor: .systemBackground))
.tint(Color(uiColor: .secondaryLabel))
}
.padding(10)
} //: END HStack
Spacer()
} //: End VStack
.background(Color.blue.ignoresSafeArea())
}
}

Image movement & animation

How can I take a image on SplashView() and make it go to the top right hand corner on Home() view during the animation between the two?
SplashView()
struct SplashView: View {
// 1.
#State var isActive:Bool = false
var body: some View {
VStack {
// 2.
if self.isActive {
// 3.
Home()
.onAppear {
print("IsVerificationSent: \(RequestedVerification)")
}
} else {
// 4.
Image("logo")
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.overlay {
RoundedRectangle(cornerRadius: 30)
.stroke(.white, lineWidth: 1)
}
.shadow(color: Color.black.opacity(0.5), radius: 7)
}
}
// 5.
.onAppear {
// 6.
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
// 7.
withAnimation {
self.isActive = true
}
}
}
}
}
Home()
struct Home: View {
var body: some View {
VStack {
Image("logo")
.resizable()
.scaledToFill()
.offset(y: 25)
.frame(width: 50, height: 50)
.overlay {
RoundedRectangle(cornerRadius: 10)
.stroke(.white, lineWidth: 1)
.offset(y: 25)
}
.shadow(radius: 2)
Spacer(minLength: 0)
}
}
}
And since I can't put the full Home() view on here, how can I go about adjusting to make it look the best possible?

Is there a way to have lazy pickers similar to a lazyvstack?

Currently, I have a popup menu that contains two wheel pickers. I have create a drag gesture to allow swiping the popup menu away. However, as I add more entries to the picker wheels, the drag gesture becomes more and more laggy. The animation is fluid if I remove one of the pickers, or if I have less entries. How can I keep the animation fluid while having a lot of entries in both pickers? I have tried using a drawingGroup, however it doesn't work with a picker. Is there something along the lines of lazy loading for pickers?
Here are the pickers on the popup:
GeometryReader { geo in
HStack(spacing: 0) {
Picker(selection: $selection1) {
ForEach(0 ..< array1.count) { index in
...
}
} label: {
}
.labelsHidden()
.pickerStyle(.wheel)
.frame(width: geo.size.width / 2)
.compositingGroup()
.clipped()
Picker(selection: $selection2) {
ForEach(0 ..< array2.count) { index in
...
}
} label: {
}
.labelsHidden()
.pickerStyle(.wheel)
.frame(width: geo.size.width / 2)
.compositingGroup()
.clipped()
}
}
Here is the popup menu containing the pickers:
VStack {
ZStack {
ZStack {
Color("whiteblack")
VStack {
Capsule()
.frame(width: 40, height: 5)
.foregroundColor(.gray)
.padding(.top, 5)
Spacer()
}
}
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
.onChanged({ value in
let amt = prevDragHeight - value.translation.height
if slideOffset >= 0 {
slideOffset -= amt
}
if slideOffset < 0 {
slideOffset = 0
}
prevDragHeight = value.translation.height
})
.onEnded({ _ in
if slideOffset > 70 {
withAnimation(.interactiveSpring()) {
slideOffset = 0
isShowing = false
}
slideOffset = 0
} else {
withAnimation(.interactiveSpring()) {
slideOffset = 0
}
}
prevDragHeight = 0
})
)
DoublePicker()
}
}
.frame(height: 400)
.frame(maxWidth: .infinity)
.background(.white)
.cornerRadius(20)
.padding()
.padding(.bottom)
.offset(x: 0, y: slideOffset)
.transition(.move(edge: .bottom))

How would I code this Tab Bar in Flutter?

I have some experience with SwiftUI and I wrote this code some time ago. I am currently learning how to use Flutter to build UI. How would I go about coding this? It's a floating Tab Bar that can open and close by swiping it.
This is basically an rounded rectangle HStack with 5 buttons inside of it that can animate into a single button if it's swiped or long pressed. I have just started with Flutter a couple of days ago and I'm still struggling a bit to convert my SwiftUI logic to Flutter. Any ideas?
struct TabBar: View {
#Binding var selected : Int
#State var expand = true
var drag: some Gesture {
DragGesture()
.onChanged {_ in self.expand = false}
}
var body: some View {
HStack {
Spacer(minLength:0)
HStack{
if !self.expand{
Button(action: {
self.expand.toggle()
}) {
Image("appleLogo")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 15, height: 15, alignment: .center)
.foregroundColor(.black)
.scaleEffect(2)
.padding()
}
}
else{
Button(action: {
self.selected = 0
}) {
ZStack {
Circle()
.frame(width: 40, height: 40)
.opacity(0)
Image(systemName: "house")
.foregroundColor(self.selected == 0 ? .black : Color("darkGray"))
.padding(.horizontal)
.scaleEffect(1.5)
}
}
Spacer(minLength:15)
Button(action: {
self.selected = 1
}) {
ZStack {
Circle()
.frame(width: 40, height: 40)
.opacity(0)
Image(systemName: "bubble.left")
.foregroundColor(self.selected == 1 ? .black : Color("darkGray"))
.padding(.horizontal)
.scaleEffect(1.5)
}
}
Spacer(minLength:15)
Button(action: {
self.selected = 2
}) {
ZStack {
Circle()
.frame(width: 40, height: 40)
.opacity(0)
Image(systemName: "plus.app")
.foregroundColor(self.selected == 2 ? .black : Color("darkGray"))
.padding(.horizontal)
.scaleEffect(1.5)
}
}
Spacer(minLength:15)
Button(action: {
self.selected = 3
}) {
ZStack {
Circle()
.frame(width: 40, height: 40)
.opacity(0)
Image(systemName: "cart")
.foregroundColor(self.selected == 3 ? .black : Color("darkGray"))
.padding(.horizontal)
.scaleEffect(1.5)
}
}
Spacer(minLength:15)
Button(action: {
self.selected = 4
}) {
ZStack {
Circle()
.frame(width: 40, height: 40)
.opacity(0)
Image(systemName: "person")
.foregroundColor(self.selected == 4 ? .black : Color("darkGray"))
.padding(.horizontal)
.scaleEffect(1.5)
}
}
}
}
.padding(.vertical,self.expand ? 8 : 6)
.padding(.horizontal,self.expand ? 20 : 6)
.background(VisualEffectView())
.background(Color("TabBarGray").opacity(0.3))
.opacity(self.expand ? 1.0 : 0.3)
.clipShape(Capsule())
.padding(30)
.onLongPressGesture {
self.expand.toggle()}
.gesture(drag)
.animation(.interactiveSpring(response:0.6, dampingFraction:0.6, blendDuration:0.6))
.frame(maxWidth: UIScreen.main.bounds.width,
maxHeight: UIScreen.main.bounds.height, alignment: .center)
}
}
}
Open
Closed
I am also new to Flutter but generally speaking I would
create a stateful widget to store the state of the button bar. Then you create the button bar as a stateless widget (using Container(child:Row(children...)
In the stateful widget you wrap the call of the stateless widget with whatever enables swiping (haven't used this so far). In there you will have something like setState(() {expanded = !expanded}) and possibly you call the stateless widget with something like
Container(
child: (expanded ? ShowBar() : Container()),
)
where you either show your stateless widget class ShowBar or an empty Container

Ternary operator issue SwiftUI

I'm using a ternary operator in my swiftui view to change the foreground color of an item.
When using this as code everything compiles normal:
Circle()
.frame(width: 10, height: 10)
.foregroundColor(item.amount < 10 ? Color.green : Color.red)
When using this, my project does not build, CPU of my Mac starts spiking, fans kicking in etc. Anyone an idea what's wrong ?
Circle()
.frame(width: 10, height: 10)
.foregroundColor(item.amount < 10 ? Color.green : (item.amount < 100 ? Color.orange : Color.red))
Complete code:
struct ContentView: View {
#ObservedObject var expenses = Expenses()
#State private var showAddExpense = false
var body: some View {
NavigationView {
List {
ForEach (expenses.items) { item in
HStack {
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.type)
}
Spacer()
Text("€\(item.amount)")
Circle()
.frame(width: 10, height: 10)
.foregroundColor(item.amount < 10 ? Color.green : (item.amount < 100 ? Color.orange : Color.red))
}
}
.onDelete(perform: removeItem)
}
.navigationBarTitle("iExpense")
.navigationBarItems(leading: EditButton(), trailing:
Button(action: {
self.showAddExpense = true
}
) {
Image(systemName: "plus")
})
}
.sheet(isPresented: $showAddExpense) {
AddView(expenses: self.expenses)
}
}
func removeItem(at index: IndexSet) {
expenses.items.remove(atOffsets: index)
}
}
Error showing on the sheet modifier, but this one is correct.
Break body construction for smaller components, like below
ForEach (expenses.items) { item in
self.listRow(for: item) // << extract !!
}
.onDelete(perform: removeItem)
,say, to private row generator function
private func listRow(for item: Item) -> some View { // << your item type here
HStack {
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.type)
}
Spacer()
Text("€\(item.amount)")
Circle()
.frame(width: 10, height: 10)
.foregroundColor(item.amount < 10 ? Color.green : (item.amount < 100 ? Color.orange : Color.red))
}
}