Embedding View in List makes View's buttons not clickable - ios

I have list of (several) products in cart. Each product has it's corresponding view created from generic view by passing product's object. View of single product in cart has two button (+ and -) to control the amount of the product in cart. Unfortunately, when products views are embedded in List (and then ForEach) these two buttons do not work because clicking on the whole product view leads to another detailed view (which I want to leave as it is). I need ForEach to be embedded in List because I want to use .onDelete modifier to easily delete the products from list.
What I want is those two buttons (+ and -) work.
List View:
List {
ForEach(Array(cartViewModel.cart.products.keys).sorted { $0.id > $1.id}, id: \.self) { product in
ProductTileForCartView(product: product)
.environmentObject(cartViewModel)
.onTapGesture {
withAnimation(.interactiveSpring(response: 0.6, dampingFraction: 0.7, blendDuration: 0.7)) {
cartViewModel.changeFocusedProductFor(product: product)
}
}
}
.onDelete(perform: cartViewModel.removeProducts)
}
SingleProductView:
HStack(alignment: .center) {
KFImage(URL(string: product.imagesURLs.first!)!)
.placeholder {
Image("product_placeholder_image")
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(RoundedRectangle(cornerRadius: 15))
}
.retry(maxCount: 3, interval: .seconds(3))
.cancelOnDisappear(true)
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding([.vertical, .trailing])
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 10) {
Text(product.company)
.font(.system(size: 14, weight: .regular, design: .rounded))
.foregroundColor(.gray)
.fixedSize(horizontal: false, vertical: true)
Text(product.name)
.font(.system(size: 22, weight: .heavy, design: .rounded))
.fixedSize(horizontal: false, vertical: true)
.foregroundColor(colorScheme == .light ? .black : .white)
Text("$\(product.price, specifier: "%.2f")")
.font(.system(size: 20, weight: .bold, design: .rounded))
.foregroundColor(.accentColor)
}
Spacer()
if includeButtonsForAmountChange {
HStack(spacing: 20) {
Button {
withAnimation {
cartViewModel.removeProductFromCart(product: product, quantity: 1)
}
} label: {
Image(systemName: "minus.square.fill")
.resizable()
.frame(width: 30, height: 30)
}
Text("\(cartViewModel.getCartProductCount(product: product))")
.font(.system(size: 22, weight: .heavy, design: .rounded))
Button {
withAnimation {
cartViewModel.addProductToCart(product: product, quantity: 1)
}
} label: {
Image(systemName: "plus.square.fill")
.resizable()
.frame(width: 30, height: 30)
}
}
}
}
.padding()
}
How the view look:

Related

How do I make my logo into a button in swiftUI?

I'm trying to get an image of a logo to be a button. I'm new to Swift/SwiftUI and the resources I've found so far seem to be outdated information. I have the image loaded in to see that I can get the image in there, and I've created a button in the location I want it, I just want to combine the two. Code is below.
struct ContentView: View {
var body: some View {
VStack {
Image("logo1024")
.resizable()
.padding()
.frame(width: 120, height: 120)
Group{
Button(action: {
print("tapped!")
}, label: {
Text("+")
.foregroundColor(.white)
.frame(width: 40, height: 40)
.background(Color.green)
.cornerRadius(15)
.padding()
})
}.frame(maxHeight: .infinity, alignment: .bottom)
}
}
}
Just place image instead of Text into button (all other modifiers on your needs), like
Group{
Button(action: {
print("tapped!")
}, label: {
Image("logo1024")
.resizable()
.padding()
.frame(width: 120, height: 120)
.foregroundColor(.white)
.frame(width: 40, height: 40)
.background(Color.green)
.cornerRadius(15)
.padding()
})
}.frame(maxHeight: .infinity, alignment: .bottom)

iOS: SwiftUI suggestion to create a view

I'm trying to make a view in SwiftUI as shown below using SwiftUI.
Below is the code I have written, but I'm not sure what will the best way to code this is. My code looks like below with hard-coded values.
var body: some View {
TestView {
VStack {
HStack {
Image("profile")
.padding(.top, 30)
.padding(.leading, 30)
Spacer()
}
HStack {
Text("UserName")
.font(.system(size: 20, weight: .bold, design: .default))
.foregroundColor(.gray)
.padding(.leading, 30)
Spacer()
}
.padding(.bottom, 1)
HStack(spacing: 0) {
Text("4")
.font(.system(size: 42, weight: .bold, design: .default))
.foregroundColor(.pink)
VStack {
Text("/10")
.font(.system(size: 25, weight: .bold, design: .default))
.foregroundColor(.gray)
Spacer()
Spacer()
Spacer()
Spacer()
}
}
Image("stars")
.resizable()
.padding(.leading, 20)
.padding(.trailing, 20)
.padding(.bottom, 5)
}
}
.frame(width: 200, height: 250, alignment: .center)
}
the output looks like this
I believe this should not be the correct way to do this, with multiple spacers, too many adjustments.
I'm new to SwiftUI, though the same UI in xib/storyboard won't take me more than 10-15mins with constraints.
Can anyone suggest a better way?
P.S. please ignore color and font size
-----------------------------Updated code----------------------------
var body: some View {
TestView {
VStack(alignment: .leading) {
Image("profile") //Async-image
.offset(x: 30)
VStack(alignment: .center) {
Text("UserName")
.font(Font(nameFont))
ZStack(alignment: Alignment(horizontal: .leading, vertical: .center)) {
HStack(alignment: .firstTextBaseline) {
Text("4")
.font(Font(bigFont))
.foregroundColor(.pink)
}
HStack(alignment: .firstTextBaseline, spacing: 0) {
Text("4")
.font(Font(bigFont))
.opacity(0)
Text("/5")
.font(Font(smallFont))
.baselineOffset((bigFont.capHeight))
.foregroundColor(.gray)
}
}
Image("stars") //view with rating logic
.resizable()
.frame(height: 30)
.padding(.leading, 20)
.padding(.trailing, 20)
.padding(.bottom, 5)
}
}
}
.frame(width: 200, height: 250, alignment: .center)
}
Could avoid Spacers and VStack
HStack(spacing: 0) {
Text("4")
.font(.system(size: 42, weight: .bold, design: .default))
.foregroundColor(.pink)
Text("/10")
.font(.system(size: 25, weight: .bold, design: .default))
.foregroundColor(.gray)
.offset(y: -20)
}

SwiftUI View is taking exceeding edges

I am learning SwiftUI and was trying to replicate an app. I ran into a problem where the view is taking up space outside the frame as well.
It looks like this:
My code for the view is:
struct LessonsScreen: View {
var body: some View {
VStack (alignment: .leading) {
HStack {
Image(systemName: "arrow.left")
.font(.system(size: 30))
Spacer()
Image(systemName: "slider.horizontal.3")
.font(.system(size: 30))
}
Text("A2 - Elementary")
.font(.system(size: 28, weight: .semibold))
.padding()
LessonCompletion(lessonNum: 1, text: "How are you?", color: .purple)
Image("discussion")
.cornerRadius(20)
.frame(width: UIScreen.main.bounds.width, alignment: .center)
LessonCompletion(lessonNum: 2, text: "Pronunciation", color: .green)
LessonCompletion(lessonNum: 3, text: "Demonstrative pronouns", color: .red)
LessonCompletion(lessonNum: 4, text: "Present continuous", color: .yellow)
Button(action: {}, label: {
Text("Get started")
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width - 150, height: 70, alignment: .center)
.background(Color.black)
.cornerRadius(10)
.frame(width: UIScreen.main.bounds.width, alignment: .center)
.padding()
})
}
}
}
Can anyone tell me where I messed up the formatting?
If you like to align the button in center in native SwiftUI way, you can use view modifier Spacer() inside HStack() instead of .frame, like this: (and same with 'discussion' Image)
...
Button(action: {}, label: {
HStack{
Spacer()
Text("Get started")
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width - 150, height: 70, alignment: .center)
.background(Color.black)
.cornerRadius(10)
.padding()
Spacer()
}
})
...
In your button replace
.frame(width: UIScreen.main.bounds.width, alignment: .center)
with
.frame(maxWidth: .infinity, alignment: .center)

How can I push both views ? Spacer() doesn't work

Im trying to push one GeometryReader as a button to the bottom of the screen but Spacer doesn't work here...
The idea is to make the app responsive to all screen sizes.
VStack {
GeometryReader { geometry in
Text("VeganFood")
.font(.system(size: geometry.size.width/12, weight: .bold))
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.frame(maxHeight: 200)
}
/// Spacer doesn't work here
GeometryReader { geometry in
Button(action: { }) {
Text("Let's get started!")
.font(.system(size: geometry.size.width/30, weight: .medium, design: .rounded))
.frame(width: geometry.size.width/3, height: geometry.size.height/7)
.foregroundColor(.white)
.background(Color.black)
.cornerRadius(20.0)
}
.frame(maxWidth: .infinity)
.frame(maxHeight: 200)
}
}
GeometryReader takes space as much as it can.
struct ContentView: View {
var body: some View {
VStack {
GeometryReader { geometry in
Text("VeganFood")
.font(.system(size: geometry.size.width/12, weight: .bold))
.foregroundColor(.red)
.frame(maxWidth: .infinity)
.frame(maxHeight: 200)
}
GeometryReader { geometry in
VStack { //<= here
Spacer() //<=here
Button(action: { }) {
Text("Let's get started!")
.font(.system(size: geometry.size.width/30, weight: .medium, design: .rounded))
}
.frame(width: geometry.size.width/3, height: geometry.size.height/7)
.foregroundColor(.white)
.background(Color.black)
.cornerRadius(20.0)
.frame(maxWidth: .infinity)
}
}
}
}
}
There might be some different approaches

How to create a 2 column grid with square shaped cells in SwiftUI

I'm trying to replicate this UI in SwiftUI using a Grid.
I created the cell like this.
struct MenuButton: View {
let title: String
let icon: Image
var body: some View {
Button(action: {
print(#function)
}) {
VStack {
icon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
Text(title)
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
.multilineTextAlignment(.center)
.padding(.top, 10)
}
}
.frame(width: 160, height: 160)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.fr_primary, lineWidth: 0.6))
}
}
And the Grid like so.
struct LoginUserTypeView: View {
private let columns = [
GridItem(.flexible(), spacing: 20),
GridItem(.flexible(), spacing: 20)
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 30) {
ForEach(Menu.UserType.allCases, id: \.self) { item in
MenuButton(title: item.description, icon: Image(item.icon))
}
}
.padding(.horizontal)
.padding()
}
}
}
But on smaller screens like the iPod, the cells are overlapped.
On bigger iPhone screens, still the spacing is not correct.
What adjustments do I have to make so that in every screen size, the cells would show in a proper square shape and equal spacing on all sides?
MenuButton has fixed width and height, thats why it behaves incorrectly.
You could utilise .aspectRatio and .frame(maxWidth: .infinity, maxHeight: .infinity) for this:
struct MenuButton: View {
let title: String
let icon: Image
var body: some View {
Button(action: {
print(#function)
}) {
VStack(spacing: 10) {
icon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 60, maxHeight: 60)
Text(title)
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
.multilineTextAlignment(.center)
}
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.aspectRatio(1, contentMode: .fill)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color. fr_primary, lineWidth: 0.6))
}
}

Resources