How to tell if a func is called? - swiftui

I'm trying to change the zIndex of an element in my Vstack only when a certain function is called/active.
My code is as follows;
Image(superReminder.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 200,maxHeight: .infinity)
.saturation(superReminder.pastreminder ? 0.1 : 1)
.clipShape(Rectangle())
.cornerRadius(10)
.padding(.all)
.pinchToZoom()
//.zIndex(pinchToZoom ? 1 : 2) // SOMETHING LIKE THIS
.pinchToZoom() is the custom function I declared in another Swift file.
Is there a way to tell if the function is called or not? Or can I declare a #State var for the function?

You could declare a #State variable in your view, pass it to the custom function so it's changed when there's a pinch, and use that variable in .zIndex()
#State var isPinched: Bool = false
Image(superReminder.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 200,maxHeight: .infinity)
.saturation(superReminder.pastreminder ? 0.1 : 1)
.clipShape(Rectangle())
.cornerRadius(10)
.padding(.all)
.pinchToZoom($isPinched)
.zIndex($isPinched ? 1 : 2)

Related

Why does putting an SF symbol in a UIImage and then in a SwiftUI Image View result in a blurry image?

I'm trying to write a public init that takes a UIImage, but I want to allow both a normal, actual UIImage made from say a photo (or otherwise drawn) OR use the SF Symbols, but I am stumped as to why the SF Symbols are blurred. What am I missing?
Here is the code I've got:
struct TempUIImageView: View {
var defaultImage: UIImage = UIImage(systemName: "globe")!
var defaultImageString: String = "globe"
var body: some View {
VStack {
Image(uiImage: defaultImage)
.resizable()
.scaledToFill()
.aspectRatio(contentMode: .fit)
Image(systemName: defaultImageString)
.resizable()
.scaledToFill()
.aspectRatio(contentMode: .fit)
}
.padding()
}
}
i'm hoping to use a solution in this:
#State private var displayedImage: UIImage?
...
private var displayImage: some View {
Image(uiImage: displayedImage!)
.resizable()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.scaledToFill()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.shadow(radius: 4)
}
But displayedImage could be a UIImage OR an SF Symbol.
(The forced unwrap is dealt with in other code, but this is preliminary code anyways)
When you use Image(systemName:) the system draws to the screen using vector graphics, resulting in sharp, un-blurry renderings.
However, UIImage, including, it seems UIImage(systemName:) is a bitmapped image, at a set resolution. If you scale it up (as you're doing in your second block of code), you'll get blurriness, because you're not getting the benefits of the vector graphics.
Using a pattern like the following would allow you to conditionally display a Image(uiImage:) or Image(systemName:) and still use the same set of modifiers for each:
struct ContentView: View {
#State var displayedImage : UIImage?
var imageToDisplay: Image {
if let displayedImage = displayedImage {
return Image(uiImage: displayedImage)
} else {
return Image(systemName: "camera.circle")
}
}
var body: some View {
imageToDisplay
.resizable()
.scaledToFill()
.aspectRatio(contentMode: .fit)
.frame(width: 400, height: 400)
}
}

How do I create a looping heartbeat animation in swiftUI?

I am trying to create an animation where a small heart icon is pumping. I have the two images I believe are sufficient to create the effect, but I have no idea how to create this animation effect. I have tried several things and none of them seem to work.
Any help you can offer will be greatly appreciated.
Here's what I have so far:
#State var show : Bool = false
var body: some View {
VStack(alignment: .trailing){
ZStack{
BlackView()
if(show){
Image("heartOrgan1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height:50)
.hidden()
Image("heartOrgan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
} else {
Image("heartOrgan1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
Image("heartOrgan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50, height: 50)
.hidden()
}
}
.onAppear(){
withAnimation { self.show.toggle()}
}
}
}
The general idea is to loop the switching between two heart images that represent a heart beating. I am interested in using these particular heart images as they look like actual hearts, and I like that.
You don't necessarily need two images for this. You can use one image and apply the scale effect on it. Make the one image scale up and add a delay to it. Then make it repeat.
Example:
#State private var animationAmount: CGFloat = 1
var body: some View {
ZStack {
Color.black
Image(systemName: "heart.fill")
.resizable()
.frame(width: 50, height: 50)
.foregroundColor(.red)
.scaleEffect(animationAmount)
.animation(
.linear(duration: 0.1)
.delay(0.2)
.repeatForever(autoreverses: true),
value: animationAmount)
.onAppear {
animationAmount = 1.2
}
}
}
You can also change the decimal value in inside the delay() to have different heartbeats. The heartbeat looks consistent with delays between 0.1 - 0.4.
I would slightly modify the great answer provided by Jame to make the animation a little bit more realistic, just by changing the linear animation for a spring animation like this:
struct ContentView: View {
#State private var animationAmount: CGFloat = 1
var body: some View {
ZStack {
Color.black
Image(systemName: "heart.fill")
.resizable()
.frame(width: 50, height: 50)
.foregroundColor(.red)
.scaleEffect(animationAmount)
.animation(
.spring(response: 0.2, dampingFraction: 0.3, blendDuration: 0.8) // Change this line
.delay(0.2)
.repeatForever(autoreverses: true),
value: animationAmount)
.onAppear {
animationAmount = 1.2
}
}
}
}
You can play with repetition, dampingFraction and blenDuration parameters to get a more customised animation.
:)

SwifUI Zstack not working in multiple devices

I have this code:
struct ContentView: View {
var body: some View {
ZStack {
Image("bird")
.resizable()
.aspectRatio(contentMode: .fill)
.edgesIgnoringSafeArea(.all)
Image(systemName: "camera.on.rectangle")
.offset(x: 160, y: -380)
.font(.largeTitle)
Image(systemName: "heart")
.offset(x: -160, y: 380)
.font(.largeTitle)
.foregroundColor(.red)
Image(systemName: "square.and.arrow.up.on.square")
.offset(x: -160, y: -380)
.font(.largeTitle)
}
}
}
I use the .offset to set the specific place to the images to be. But when I change to another device format they move away. Is there a way to make constrains or better this code to function properly in SwiftUI?
Thanks.

How do I encapsulate an image in a shape?

The following code snippet yields a Cannot invoke initializer for type 'Circle' with no arguments error on the .clipShape() line. I've tried shuffling around the modifiers to no avail. Any suggestions for fixing? Thanks!
struct MedCircleImage: View {
var image: Image
var body: some View {
image
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.overlay(Circle().stroke(periwinkle, lineWidth: 3))
.frame(width: 86, height: 86)
}
}
try this:
you did not define your periwinkle variable...unfortunately SwiftUI does not always show the "real" error
struct ContentView: View {
var image: Image = Image("laguna")
var body: some View {
image
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.overlay(Circle().stroke(lineWidth: 3))
.frame(width: 86, height: 86)
}
}

How to make width of view equal to superview in SwiftUI

I have Button
struct ContentView : View {
var body: some View {
HStack {
Button(action: {}) {
Text("MyButton")
.color(.white)
.font(Font.system(size: 17))
}
.frame(height: 56)
.background(Color.red, cornerRadius: 0)
}
}
}
But I want to pin it to supreview's edges (trailing to superview's trailing and leading). Like this:
HStack doesn't help me, and it's expecting.
Fixed frame or width equals UIScree.size are not flexible solutions.
You need to use .frame(minWidth: 0, maxWidth: .infinity) modifier
Add the next code
Button(action: tap) {
Text("Button")
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.red)
}
.padding(.horizontal, 20)
Padding modifiers will allow you to have some space from the edge.
Keep in mind that the order of modifiers is essential. Because modifiers are functions that are wrapping the view below (they do not change properties of views)
You can use GeometryReader: https://developer.apple.com/documentation/swiftui/geometryreader
According to Apple:
This view returns a flexible preferred size to its parent layout.
It is a flexible solution, as it changes according to the parent layout changes.
Here's a pure SwiftUI solution where you want to fill the width of the parent but constrain the height to an arbitrary aspect ratio (say you want square images):
Image("myImage")
.resizable()
.scaledToFill()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.aspectRatio(16.0 / 9.0, contentMode: .fit)
Just replace 16.0 / 9.0 with whatever aspect ratio you want.
I spent about a day trying to figure this out because I didn't want to use GeometryReader or UIScreen.main.bounds (which isn't cross-platform)
Edit: Found an even simpler way.
Simple adding following to your code will make the button extend edge to edge. (You can add padding as well if you want)
.frame(minWidth: 0, maxWidth: .infinity)
The entire Button code will look like this:
struct ContentView : View {
var body: some View {
HStack {
Button(action: {}) {
Text("MyButton")
.color(.white)
.font(Font.system(size: 17))
}
.frame(minWidth: 0, maxWidth: .infinity)
.padding(.top, 8)
.padding(.bottom, 8)
.background(Color.red, cornerRadius: 0)
}
}
}
I found one solution:
var body: some View {
Button(action: {}) {
HStack {
Spacer()
Text("MyButton")
.color(.white)
.font(Font.system(size: 17))
Spacer()
}
.frame(height: 56)
.background(Color.red, cornerRadius: 0)
}.padding(20)
}
But I'm not sure that it is the best. May be someone will find more elegant solution/