SwiftUI - Move and rotate an Image when a button is pressed - image

I have an Image, and I want it to rotate on the y axis, and move towards the bottom of the View when I press a Button. I tried using the .onChange, but I get the error "Result of call to 'animation' is unused", of which I understand the meaning, but I don't understand neither why it comes up nor how can I fix it.
Here's my code:
import SwiftUI
struct ContentView : View {
#State var animateCoin = false
#Environment(\.colorScheme) var colorScheme
var body: some View {
Rectangle()
.foregroundColor(colorScheme == .dark ? .white : .black)
.frame(width: 150, height: 150, alignment: .center)
.offset(y: -60)
.mask(Image("Coin") //That's the name of the Image I have in the assets
.resizable()
.onChange(of: animateCoin, perform: { value in
self.animation(.easeIn) //I put .easeIn just as an example, because the .rotation3DEffect gives me a red warning
}))
Button(action: { animateCoin = true } ) {
ZStack {
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.green)
.frame(width: 100, height: 40, alignment: .center)
.shadow(radius: 10)
Text("Animate")
.foregroundColor(.white)
.shadow(radius: 20)
}
}
}}
The Image is set as a mask so that I can easily control its color depending on the light or dark mode.
Thank to everyone who will help me!

How about doing it like this:
#State var animateCoin = false
#Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
Rectangle()
.foregroundColor(colorScheme == .dark ? .white : .black)
.frame(width: 150, height: 150, alignment: .center)
.offset(y: -60)
.mask(Image(systemName: "car.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.rotation3DEffect(
Angle(degrees: self.animateCoin ? 360 : 0),
axis: (x: 0, y: self.animateCoin ? 360 : 0, z: 0)
)
)
.offset(y: self.animateCoin ? 600 : 0)
.animation(.linear(duration: 1))
ZStack {
Button(action: { self.animateCoin.toggle() } ) {
ZStack {
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.green)
.frame(width: 100, height: 40, alignment: .center)
.shadow(radius: 10)
Text("Animate")
.foregroundColor(.white)
.shadow(radius: 20)
}
}
}
}
}
As you asked:
Rotate on Y axis.
Move the image to the buttom.

Related

How to use swiftUI matched geometry to present a detail card view on top of elements (instead of increasing frame height)

I am attempting my first steps using matchedgeometry and have followed some tutorials to get a simple view to expand when selected.I have two files, a card view and a content view. I would like to place all of my card views in a horizontal scrollview.
import SwiftUI
struct smallCard: View {
#State private var flag: Bool = true
#Namespace var nspace
var body: some View {
if flag {
VStack{
Image("chemex.jpg")
.resizable()
.matchedGeometryEffect(id: "image", in: nspace)
.scaledToFill()
Spacer()
Button("CHEMEX") { withAnimation(.default) { flag.toggle() } }
.matchedGeometryEffect(id: "text", in: nspace)
Spacer()
.frame(height:8)
}
.matchedGeometryEffect(id: "geoeffect1", in: nspace)
.frame(width: 100, height: 100, alignment: .center)
.background(Color.white)
.cornerRadius(20)
.shadow(color: .gray, radius: 9, x: 0, y: 9)
}
if !flag {
VStack{
Image("chemex.jpg")
.resizable()
.matchedGeometryEffect(id: "image", in: nspace)
.scaledToFit()
Spacer()
Button("CHEMEX") { withAnimation(.default) { flag.toggle() } }
.matchedGeometryEffect(id: "text", in: nspace)
Spacer()
.frame(height:20)
}
.matchedGeometryEffect(id: "geoeffect1", in: nspace)
.layoutPriority(1)
.frame(width: 300, height: 600, alignment: .center)
.background(Color.white)
.cornerRadius(20)
.shadow(color: .gray, radius: 9, x: 0, y: 9)
}
}
I the have my main content view:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack{
HStack{
Text("My App")
.font(.title)
.padding(.leading, 25)
Spacer()
}
ScrollView(.horizontal){
HStack{
smallCard()
.padding(.horizontal)
smallCard()
.padding(.horizontal)
smallCard()
.padding(.horizontal)
}
}
}
.frame(alignment: .center)
}
This currently works for clicking on the small card and the matched geometry animates this to the larger card. The issue I have is that this changes the height of my scrollview and as such this moves everything else such as my title up and out of view.
Effectively what I would like to achieve is like a popover view, so that this detail view is on top of all of the elements in my content view, but whilst using matchedgeometry (or an alternative solution) to animate nicely between the views.
Is this possible? I am at the point where I do not know if there is a solution to this.

How to align bottom edge to bottom trailing of above view SwiftUI

I'm currently trying to align the person image bottom trailing to the bottom edge of the image in a SwiftUI View.
Code:-
struct AddSecondImageOnSameImage: View {
#State var image = Image(systemName: "person.circle.fill")
var body: some View {
VStack{
self.image
.renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
.padding(1)
.overlay(Circle()
.frame(width: 20, height:20)
.foregroundColor(Color.init(UIColor.init(displayP3Red: 255/255, green: 92/255, blue: 210/255, alpha: 1.0))),alignment: .bottomTrailing)
}
}
}
Output:-
]
Want to achieve:-
Can someone please explain to me how to align the person image bottom trailing to the bottom edge of the image in a SwiftUI View. I've tried to implement by above but no results yet.
Any help would be greatly appreciated.
Thanks in advance.
Use ZStack instead of .overlay()
The ZStack assigns each successive child view a higher z-axis value than the one before it, meaning later children appear “on top” of earlier ones.
https://developer.apple.com/documentation/swiftui/zstack
struct AddSecondImageOnSameImage: View {
#State var image = Image(systemName: "person.circle.fill")
var body: some View {
ZStack(alignment: .bottomTrailing) {
self.image
.renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
.padding(1)
Circle()
.frame(width: 20, height:20)
.foregroundColor(Color.init(UIColor.init(displayP3Red: 255/255, green: 92/255, blue: 210/255, alpha: 1.0)))
.offset(x: -5, y: -5)
}
}
}
You may need to adjust the offset.
By using a Z-Stack and defining the frame, you can align the views inside by manipulating their frame using .infinity and setting the alignment to the desired outcome. For example in your scenario:
import SwiftUI
struct SampleView: View {
#State var image = Image(systemName: "person.circle.fill")
var body: some View {
ZStack {
self.image
.renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100, alignment: .center)
.clipShape(Circle())
.padding(1)
Circle()
.frame(width: 20, height:20)
.foregroundColor(.green)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing)
}
.frame(width: 100, height: 100)
}
}
There are several ways to implement it but setting the frame and the alignment is required. Avoid using offset constants since this would not work the same everywhere, for example, in responsive resizing.
SampleView

How to get round shape images of the same size based on SF Symbols in SwiftUI?

In my application I want to get simple round buttons based on SF Symbols of the same size. However, the same approach results in different image sizes depending on the symbol.
For example, an image with a plus sign is larger than a minus sign.
To solve this problem, I use the ZStack trick in which I put a transparent plus under the minus. But I think this is not the best solution. Are there any better solutions?
HStack{
Image(systemName: "plus")
.padding()
.overlay(
Circle()
.stroke(Color.primary,
lineWidth:1))
Image(systemName: "minus")
.padding()
.overlay(
Circle()
.stroke(Color.primary,
lineWidth:1))
//my solution
ZStack {
Image(systemName: "plus")
.padding()
.opacity(0.0)
.overlay(
Circle()
.stroke(Color.primary,
lineWidth:1))
Image(systemName: "minus")
}
}
"minus" in the center has a smaller size than "plus", "minus" on the right - my solution:
You can use ViewModifier or if are buttons ButtonStyle
ViewModifier
#available(iOS 13.0, *)
struct fillButtonCircle: ViewModifier {
var foregroundColor: Color = .white
var backgroundColor: Color = .green
var dimension: CGFloat = 10
func body(content: Content) -> some View {
content
.foregroundColor(foregroundColor)
.padding(dimension)
.background(backgroundColor)
.clipShape(Circle())
.frame(width: dimension, height: dimension)
.overlay(
Circle()
.stroke(Color.primary,
lineWidth:1))
}
}
ButtonStyle
#available(iOS 13.0, *)
struct CircleScaleButton: ButtonStyle {
var color: Color = .blue
var maxHeight: CGFloat = 35
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: maxHeight, alignment: .center)
.foregroundColor(.black)
.background(RoundedRectangle(cornerRadius: 35/2.0).fill(self.color))
.compositingGroup()
.clipShape(Circle())
.overlay(
Circle()
.stroke(Color.primary,
lineWidth:1))
.opacity(configuration.isPressed ? 0.8 : 1.0)
.scaleEffect(configuration.isPressed ? 0.9 : 1.0)
}
}
Example
struct SwiftUIViewTest: View {
var body: some View {
VStack {
Text("Button")
HStack {
Button(action: {}, label: {
Image(systemName: "plus")
})
.buttonStyle(CircleScaleButton(color: .clear, maxHeight: 45))
Button(action: {}, label: {
Image(systemName: "minus")
})
.buttonStyle(CircleScaleButton(color: .clear, maxHeight: 45))
}
Spacer()
.frame(height: 50)
Text("Image")
HStack {
Image(systemName: "plus")
.modifier(fillButtonCircle(foregroundColor: .black, backgroundColor: .clear, dimension: 40))
Image(systemName: "minus")
.modifier(fillButtonCircle(foregroundColor: .black, backgroundColor: .clear, dimension: 40))
}
}
}
}
use .circle
import SwiftUI
struct ContentView: View {
var body: some View {
HStack {
Image(systemName: "plus.circle")
.resizable()
.frame(width: 50, height: 50, alignment: .center)
Image(systemName: "minus.circle")
.resizable()
.frame(width: 50, height: 50, alignment: .center)
}
}
}

Problem with formatting LazyHGrid - SwiftUI

I am trying to create a basic lazyHGrid but after looking at a couple of different tutorials online I still can't manage to build it properly. I am trying to make a grid that is filled with rectangles with symbols and text inside those rectangles. See image below:
If I make this in a HStack all the rectangles are presented correctly, but as soon as I put it into a LazyHGrid I get this problem. Any ideas?
let rows = [
GridItem(.fixed(200)),
GridItem(.fixed(200)),
]
var product: ProductModel
var body: some View {
LazyHGrid(rows: rows, alignment: .center) {
ForEach(0..<product.imageStack01Image.count, id: \.self) { item in
ZStack(alignment: .top){
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color.white)
.frame(width: 200, height: 200)
.shadow(color: Color.black.opacity(0.08), radius: 20, x: 0, y: 5)
VStack(alignment: .leading) {
Image(systemName:product.imageStack01Image[item])
.font(.system(size: 40.0))
.frame(height: 40)
.foregroundColor(.red)
.padding(.top, 40)
.padding(.leading, 10)
Text(product.imageStack01Text[item])
.font(.title2)
.fontWeight(.medium)
.multilineTextAlignment(.trailing)
.padding(10)
}
}
}
}
}
As far as I understood your try to put image and text into rectangle, so try to use overlay instead of ZStack
ForEach(0..<product.imageStack01Image.count, id: \.self) { item in
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color.white)
.frame(width: 200, height: 200)
.shadow(color: Color.black.opacity(0.08), radius: 20, x: 0, y: 5)
.overlay(
VStack(alignment: .leading) {
Image(systemName:product.imageStack01Image[item])
.font(.system(size: 40.0))
.frame(height: 40)
.foregroundColor(.red)
.padding(.top, 40)
.padding(.leading, 10)
Text(product.imageStack01Text[item])
.font(.title2)
.fontWeight(.medium)
.multilineTextAlignment(.trailing)
.padding(10)
})
}
}
It might have to do with the hardcoded frame in your square. Try using aspectRatio if you just want a square or frame each layer of the ZStack or frame the ZStack depending on what you want to achieve.
import SwiftUI
struct LazyGridSample: View {
///Sample Products to reproduce
#State var products = ["0xxxxxxxxxx0xxxxxxxxxx","1xxxxxxxxxx0xxxxxxxxxx","2xxxxxxxxxx0xxxxxxxxxx","3xxxxxxxxxx0xxxxxxxxxx","4xxxxxxxxxx0xxxxxxxxxx","5xxxxxxxxxx0xxxxxxxxxx","6xxxxxxxxxx0xxxxxxxxxx","7xxxxxxxxxx0xxxxxxxxxx","8xxxxxxxxxx0xxxxxxxxxx","9xxxxxxxxxx0xxxxxxxxxx"]
let rows = [
GridItem(.fixed(200)),
GridItem(.fixed(200)),
]
let squareSize: CGSize = CGSize(width: 200, height: 200)
var body: some View {
//ScrollView(.horizontal){
LazyHGrid(rows: rows, alignment: .center) {
//HStack{
ForEach(0..<products.count, id: \.self) { idx in
ZStack(alignment: .top){
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color.white)
//.frame(width: squareSize.width, height: squareSize.height)
//.aspectRatio(1, contentMode: .fill)
.shadow(color: Color.black.opacity(0.08), radius: 20, x: 0, y: 5)
VStack(alignment: .leading) {
//Removed Image
Text(products[idx].description)
.font(.title2)
.fontWeight(.medium)
.multilineTextAlignment(.trailing)
.padding(10)
}//.frame(width: squareSize.width, height: squareSize.height, alignment: .center)
//.aspectRatio(1, contentMode: .fill)
}//Adjusts the the entire ZStack
//.frame(width: squareSize.width, height: squareSize.height, alignment: .center)
.aspectRatio(1, contentMode: .fill)
}
}
//}//ScrollView
}
}
struct LazyGridSample_Previews: PreviewProvider {
static var previews: some View {
LazyGridSample()
}
}

SwiftUI animations of elements in a scrollview doesn't work ? Xcode GM update

I just switched to the GM version of xcode and since then I have a problem that I did not have before it seems to me.
I created a simplified version of the problem:
I have a scrollview with several elements inside.
I add animations states on the blue square but I have the impression the elements have no animations and changes state brutally.
I tried with an element outside the scrollview (purple square) and it works
I don't see why animations do not work someone has an idea?
#State var Enter = false
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 15) {
Rectangle()
.foregroundColor(Color.red)
.frame(width: 80, height: 80, alignment: .center)
Button(action: {
withAnimation(.easeInOut(duration: 1.12)) {
self.Enter = true
}
}) {
Rectangle()
.foregroundColor(Color.blue)
.frame(width: 80, height: 80, alignment: .center)
.opacity(self.Enter ? 0 : 1)
}
//.padding(.horizontal, self.Enter ? 50 : 10)
Rectangle()
.foregroundColor(Color.green)
.frame(width: 80, height: 80, alignment: .center)
.offset(x: self.Enter ? 30 : 0 , y: 0)
Rectangle()
.foregroundColor(Color.red)
.frame(width: 80, height: 80, alignment: .center)
}
.padding(.leading, 67 )
.padding(.trailing, 110)
// .padding(.top, (screen.height)/81.2)
.padding(.bottom, 10)
}
HStack {
Rectangle()
.foregroundColor(Color.purple)
.frame(width: 80, height: 80, alignment: .center)
.offset(x: self.Enter ? 80 : 0 , y: 0)
}
}
}
Using implicit vice explicit animation often works for me in these situations. This should accomplish what you were looking for: (works in the Xcode 11 GM seed)
Update: GM seed is apparently not passing the animation inside the scroll view. Edited to apply animation to both the HStack and the lone purple box
struct Square: View {
let color: Color
var body: some View {
Rectangle()
.fill(color)
.frame(width: 80, height: 80)
}
}
struct SquareAnimation: View {
#State private var enter = false
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 15) {
Square(color: .red)
Square(color: .blue)
.opacity(self.enter ? 0.25 : 1)
.onTapGesture {
self.enter.toggle()
}
Square(color: .green)
.offset(x: self.enter ? 30 : 0)
Square(color: .red)
}
.animation(.easeInOut)
}
Square(color: .purple)
.offset(x: self.enter ? 80 : 0)
.animation(.easeInOut)
}
}
}
I am stuck with same problem. Solution by smr has helped. However, I was not able to get show/hide view animation. Following is an example:
struct Test: View {
#State var showView = false
var body: some View {
ScrollView {
Button(action: {
self.showView.toggle()
}) {
Text("Toggle View")
}
if showView {
Text("Some View")
}
}
.animation(.easeInOut)
}
}

Resources