WrappingHStack doesn't animate (transition) into view - ios

I have a view with a WrappingHStack in it. The view has withAnimation when it is displayed, with .transition(.move(edge: .bottom)) as the transition on it.
WrappingHStack: https://github.com/dkk/WrappingHStack
In the following code, everything in the view (e.g. the Text("Lorem ipsum")) correctly transitions with the slide. Except for the WrappingHStack. I added .transition(.slide), still nothing. It just appears instantly in place while everything around it slides in. Why is this, and how do I get it to slide alongside the other content?
VStack {
Text("Lorem ipsum")
.font(.system(size: 18, weight: .medium, design: .rounded))
.foregroundStyle(.black)
WrappingHStack(0..<self.words.count, id:\.self, alignment: .center) { i in
Button(action: {
//
}) {
Text(words[i])
.font(.system(size: 18, weight: .bold, design: .rounded))
.foregroundColor(.white)
}
.padding(.bottom, 10)
}
.transition(.slide)
}

Related

SwiftUI Stack fill remaining height

I am a beginner in Swift and in iOS development. I am currently trying to create a sing in form with linear background on the top.
I've placed background view to gradient variable. Now I am trying to place my form after background on the top. But it doesn't fill the remaining space. I've tried using Spacer, but then form aligns to the bottom of the screen. How can I expand and align VStack with the form right after the gradient and make it fill the remaining height of the screen.
Here is my code
var gradient: some View {
ZStack(alignment: .top) {
LinearGradient(
stops: [
.init(color: .red, location: 0.4),
.init(color: .orange, location: 0.7),
.init(color: .yellow, location: 0.8)
],
startPoint: .leading,
endPoint: .trailing)
.frame(maxWidth: .infinity, maxHeight: 300, alignment: .top)
.mask(
RoundedRectangle(cornerRadius: 96)
.fill(Color.black)
.frame(width: .infinity, height: .infinity)
)
.blur(radius: 64)
}
.ignoresSafeArea()
.frame(height: .infinity, alignment: .top)
.offset(y: -100)
}
var body: some View {
VStack() {
gradient
Spacer()
VStack () {
VStack (spacing: 8) {
Text("Sign In")
.font(.title)
.fontWeight(.bold)
Text("Access to your account")
.font(.callout)
.foregroundColor(.secondary)
}
VStack (spacing: 18) {
TextField("Username", text: $username)
.modifier(TextFieldOutlined())
TextField("Password", text: $password)
.modifier(TextFieldOutlined())
}
.padding()
}
}
.frame(height: .infinity)
.padding(.bottom, 48)
.ignoresSafeArea()
}
Now it looks like this. With form Stack selected. It don't know why it is not filling the remaining height.
If I understand what you're looking for, you'd like the top portion of the screen to be a fixed size for the gradient, and then the form centered in the remaining part of the screen. You could do this by making the VStack expand using Spacers, modifying the body code to:
var body: some View {
VStack() {
gradient
VStack () {
// Add spacing above the form
Spacer()
VStack (spacing: 8) {
Text("Sign In")
.font(.title)
.fontWeight(.bold)
Text("Access to your account")
.font(.callout)
.foregroundColor(.secondary)
}
VStack (spacing: 18) {
TextField("Username", text: $username)
.modifier(TextFieldOutlined())
TextField("Password", text: $password)
.modifier(TextFieldOutlined())
}
.padding()
// Add spacing below the form
Spacer()
}
}
.padding(.bottom, 48)
.ignoresSafeArea()
}
Some of the padding, as currently written, will keep the form from being perfectly vertically centered, so you may want to tweak that.
Without your TextFieldOutlined() modifier, it looks like this in my preview (gradient is selected and outlined for emphasis):

Embedding View in List makes View's buttons not clickable

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:

SwiftUI - How to align elements in left, center, and right within HStack?

I'm creating an iOS minesweeper game and want to have a bar at the top with three pieces of information:
The high score (next to a trophy symbol) [LEFT]
The current time (next to a timer symbol) [CENTER]
The number of bombs left (next to a bomb symbol) [RIGHT]
However, the elements are not evenly aligned as you can see in the picture at the bottom- I'm guessing the spacers are automatically the same size. So, because the text on the left side of the screen is wider than that on the right, the middle text is offset to the right.
How can I align these items so the center time/image is perfectly in the middle with the other objects on the left/right?
My code looks like:
struct GameView: View {
#EnvironmentObject var game: Game
#State var labelHeight = CGFloat.zero
var body: some View {
VStack(alignment: .center, spacing: 10) {
Text("MINESWEEPER")
.font(.system(size: 25, weight: .bold, design: .monospaced))
.kerning(3)
.foregroundColor(.red)
HStack(alignment: .center, spacing: 5) {
Image(systemName: "rosette")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text(game.highScoreString)
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
Spacer()
Image(systemName: "timer")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text(game.timerString)
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
Spacer()
Image("top_bomb")
.resizable()
.aspectRatio(contentMode: .fit)
Text(String(game.bombsRemaining))
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
.overlay(GeometryReader(content: { geometry in
Color.clear
.onAppear(perform: {self.labelHeight = geometry.frame(in: .local).size.height})
}))
}
.frame(width: UIScreen.main.bounds.width * 0.9, height: labelHeight, alignment: .center)
BoardView()
}
}
}
Instead of aligning by spacers, move each part into separate view, say LeftHeader, CenterHeader, and RightHeader and use frame alignment, which gives exact separation by equal sizes, like
HStack {
LeftHeader()
.frame(maxWidth: .infinity, alignment: .leading)
CenterHeader()
.frame(maxWidth: .infinity, alignment: .center)
RightHeader()
.frame(maxWidth: .infinity, alignment: .trailing)
}
This can be achieved using a ZStack , HStack and Spacers.
ZStack{
HStack{
Text("Left")
Spacer()
}
HStack{
Text("Center")
}
HStack{
Spacer()
Text("Right")
}
}
Even if you remove left ( or right ) HStack, it won't affect the other components or the layout.
You can make it look bit nicer by adding paddings like this,
ZStack{
HStack{
Text("Left").padding([.leading])
Spacer()
}
HStack{
Text("Center")
}
HStack{
Spacer()
Text("Right").padding([.trailing])
}
}
I think you can create more one stack.
My solution like:
/**
* spacing is an example
* minWidth calculated by fontSize
*/
HStack(alignment: .center, spacing: 20){
HStack{ Image, Text }.frame(minWidth: 30)
HStack{ Image, Text }.frame(minWidth: 30)
HStack{ Image, Text }.frame(minWidth: 30)
}
Hope it useful for you.

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

SwiftUI TabView Issue

I'm building an app and i'm a fairly beginner in SwiftUI, so this is the problem I have.
I need a landing page which shows first and I've created a view for it you can check it out.
struct LandingView: View {
var body: some View {
NavigationView{
VStack(alignment: .center){
Image("exxxlogo")
Spacer()
Text("Welcome")
.font(.system(size: 38))
.fontWeight(.bold)
.padding(.horizontal, 40)
.multilineTextAlignment(.center)
.foregroundColor(Color.white)
Text("Click below to continue to the app")
.font(.system(size: 16))
.padding(.horizontal, 20)
.multilineTextAlignment(.center)
.foregroundColor(Color.white)
.padding(.bottom, 35)
NavigationLink(destination: HomeView()){
Text("CONTINUE ")
.frame(width: 250, height: 50, alignment: .center)
.background(Color.red)
.foregroundColor(Color.white)
}
}
.background(
Image("backgroundlanding")
.frame(maxWidth: .infinity,maxHeight: .infinity)
.aspectRatio(contentMode: .fill)
.edgesIgnoringSafeArea(.top)
.edgesIgnoringSafeArea(.bottom)
)
}
And I've also created the views for the tabview to redirect them to so Home,Shop, etc
I've done this to my Contentview
TabView {
HomeView()
.tabItem{
Image(systemName: "house")
Text("Home")
}
GamesView()
.tabItem{
Image(systemName: "gamecontroller")
Text("Games")
}
ShopView()
.tabItem{
Image(systemName: "bag")
Text("Shop")
}
MoreView()
.tabItem{
Image(systemName: "gear")
Text("More")
}.accentColor(.red)
And changed the windowgroup to landingview in the main app.swift file so it pops up first.
But the issue is when you click on the button in landingview, navigationlink redirects you to the homeview but there is no tabviews underneath just blank
You said that your second code example is from ContentView. If you want to show the TabView that you have in that ContentView, then your NavigationLink should point to ContentView, not HomeView.
NavigationLink(destination: ContentView()) {
Text("CONTINUE ")
.frame(width: 250, height: 50, alignment: .center)
.background(Color.red)
.foregroundColor(Color.white)
}

Resources