I want to mask a linear gradient with a Image initialized with Image(uiImage: ...). This kind of masking can clearly be done with Images initialized as systemName but when done with a UIImage there is no "masking" performed. Note that I need to use UIImage because my intention is to use a generated qrcode as the mask.
Correctly Masked sf image:
Incorrectly masked UIImage:
Exmaple Code:
struct ContentView: View {
let string: String = "String"
let context = CIContext()
let filter = CIFilter.qrCodeGenerator()
let size: CGFloat = 150
func generateQRCode(string: String) -> UIImage? {
let data = Data(string.utf8)
filter.setValue(data, forKey: "inputMessage")
if let qrCodeImage = filter.outputImage {
if let qrCodeCGImage = self.context.createCGImage(qrCodeImage, from: qrCodeImage.extent) {
return UIImage(cgImage: qrCodeCGImage)
}
}
return nil
}
var body: some View {
VStack {
// sf image qr code
GradientRectangle()
.mask {
Image(systemName: "qrcode")
.resizable()
}
.frame(width: size, height: size)
// uiimage qr code
if let image = generateQRCode(string: string) {
GradientRectangle()
.mask {
Image(uiImage: image)
.interpolation(.none)
.resizable()
}
.frame(width: size, height: size)
}
}
}
}
struct GradientRectangle: View {
var body: some View {
Rectangle()
.fill(LinearGradient(colors: [.yellow, .orange], startPoint: .topLeading, endPoint: .bottomTrailing))
}
}
The generated qrCodeImage has no transparency (as you expected) but black& white, so it is just needed to convert it additionally into mask.
Tested with Xcode 13.2 / iOS 15.2
Here is fixed part of code:
func generateQRCode(string: String) -> UIImage? {
let data = Data(string.utf8)
filter.setValue(data, forKey: "inputMessage")
if let qrCodeImage = filter.outputImage {
let maskFilter = CIFilter.maskToAlpha()
maskFilter.setDefaults()
maskFilter.setValue(qrCodeImage, forKey:"inputImage")
if let maskImage = maskFilter.outputImage, let qrCodeCGImage = self.context.createCGImage(maskImage, from: maskImage.extent) {
return UIImage(cgImage: qrCodeCGImage)
}
}
return nil
}
Related
I need to render a SwiftUI view into an image with opacity, so that empty space of the view would be transparent when I layer the image above some background.
I use this code for conversion:
func convertViewToData<V>(view: V, size: CGSize) -> UIImage? where V: View {
guard let rootVC = UIApplication.shared.windows.first?.rootViewController else {
return nil
}
let imageVC = UIHostingController(rootView: view.edgesIgnoringSafeArea(.all))
imageVC.view.frame = CGRect(origin: .zero, size: size)
rootVC.view.insertSubview(imageVC.view, at: 0)
let uiImage = imageVC.view.asImage(size: size)
imageVC.view.removeFromSuperview()
return uiImage
}
extension UIView {
func asImage(size: CGSize) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.opaque = false
return UIGraphicsImageRenderer(bounds: bounds, format: format).image { context in
layer.render(in: context.cgContext)
}
}
}
extension View{
func convertToImage(size: CGSize) -> UIImage?{
convertViewToData(view: self, size: size)
}
}
And this code to test the resulting image:
struct ContentView: View {
var view: some View{
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
}
var body: some View {
HStack{
view
Image(uiImage: view.convertToImage(size: .init(width: 200, height: 200))!)
}
.background(LinearGradient(stops: [.init(color: .green, location: 0), .init(color: .red, location: 1)], startPoint: .bottom, endPoint: .top))
}
}
This code produces two instances of the text: the one on the left is layered on the gradient background, and the one on the right is on the black background:
Clearly, the transparent parts are replaced by the black color in the image.
I figured out that the alpha information is discarded somewhere in the convertViewToData, but I was not able to find a way to preserve it.
I have created a qr code generator and i want to do time set in the qr code, so i used a date picker for time and it only shows hours and minutes. i want to use the date picker to show the result in qr code like this (12 34).
I have tried with the string but i don't know to do with the datetime
Code for string(Is working in XCode)
import Foundation
import SwiftUI
import CoreImage.CIFilterBuiltins
struct Generate: View {
#State var start = String
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
var body: some View {
Image(uiImage: imageGenerate(start))
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}
func imageGenerate(_ start: Int?)-> UIImage {
let data = Data(start)
filter.setValue(data, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
Code for datetime(not working)
import Foundation
import SwiftUI
import CoreImage.CIFilterBuiltins
struct Generate: View {
#State var start = Timer()
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
var body: some View {
Image(uiImage: imageGenerate(start))
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}
func imageGenerate(_ start: Timer)-> UIImage {
let data = Data(start.[not utf8, don't know what should it be?])
filter.setValue(data, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
Update Part
code for date picker
#State private var startTime = Date()
var body: some View {
Form{
Section {
VStack{
Text("Please Select Start Time")
.padding(.bottom, 10)
.font(.system(size:20))
.bold()
DatePicker("", selection: $startTime, displayedComponents: .hourAndMinute)
.labelsHidden()
.datePickerStyle(.wheel)
}
VStack{
Picker(selection: $sMinutes, label: Text("Please Select Minutes"))
{
ForEach(0 ..< minutes.count) {
index in Text(self.minutes[index]).tag(index)
}
}
}
}
Button("Complete"){
self.showflag.toggle()
}
.font(.system(size:20))
.bold()
.foregroundColor(Color.blue)
.frame(maxWidth: .infinity)
}
Try this example code to show a qr code using a date picker.
The code uses a DatePicker to select the hours and minutes.
Then a DateFormatter to convert those into a string for the qr code generator.
struct ContentView: View {
#State var start = Date()
var body: some View {
VStack {
DatePicker("", selection: $start, displayedComponents: [.hourAndMinute])
.datePickerStyle(.wheel)
Generate(start: $start)
}
}
}
struct Generate: View {
#Binding var start: Date // <-- here
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
var dateFormatter: DateFormatter { // <-- here
let df = DateFormatter()
df.dateFormat = "HH:mm"
return df
}
var body: some View {
Image(uiImage: imageGenerate(start))
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}
func imageGenerate(_ start: Date)-> UIImage {
let str = dateFormatter.string(from: start) // <-- here
let data = str.data(using: .utf8) // <-- here
filter.setValue(data, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
EDIT-1:
to put the qr code generation into a function, try this:
struct ContentView: View {
#State var start = Date()
#State var img: UIImage = UIImage() // <-- here
var body: some View {
VStack {
DatePicker("", selection: $start, displayedComponents: [.hourAndMinute])
.datePickerStyle(.wheel)
Button("Show QR code", action: {
img = imageGenerate(start) // <-- here
})
Image(uiImage: img)
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}
}
func imageGenerate(_ start: Date) -> UIImage {
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
let dateFormatter = DateFormatter() // <-- here
dateFormatter.dateFormat = "HH:mm" // <-- here
let str = dateFormatter.string(from: start) // <-- here
let data = str.data(using: .utf8) // <-- here
filter.setValue(data, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
I am trying to make a dynamic image selection with the help of an array, but the problem I have is that I select the first position and the photo loads in the first position, but if I select another position, the image always loads in the first position.
I am occupying a single .sheet within the foreach, it is worth mentioning that what I want to achieve is a number of images, not a fixed size
code:
import SwiftUI
struct Research {
var id: Int
var name: String
var date: String
var aux: Int
var imagen1: UIImage
var imagen2: UIImage
var imagen3: UIImage
var imagen4: UIImage
var imagen5: UIImage
var imagen6: UIImage
}
struct ContentView: View {
#State var dataResearch : [Research] = []
#State var aux: Int = 0
#State var showPicker: Bool = false // I imagine that this variable must be unique per element
#State var sourceType: UIImagePickerController.SourceType = .photoLibrary
var body: some View {
VStack{
ForEach($dataResearch, id: \.aux) { $i in
VStack{
Image(uiImage: i.imagen1)
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.edgesIgnoringSafeArea(.all)
.onTapGesture(){
showPicker = true
}
Image(uiImage: i.imagen2)
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.edgesIgnoringSafeArea(.all)
.onTapGesture(){
showPicker = true
}
}
.sheet(isPresented: $showPicker) {
ImagePicker(sourceType: sourceType, selectedImage: $i.imagen1)
.ignoresSafeArea()
}
}
}
.onAppear(){
getRequest()
}
}
func getRequest(){
let image1: UIImage = UIImage(named: "default_image")!
let image2: UIImage = UIImage(named: "default_image")!
let image3: UIImage = UIImage(named: "default_image")!
let image4: UIImage = UIImage(named: "default_image")!
let image5: UIImage = UIImage(named: "default_image")!
let image6: UIImage = UIImage(named: "default_image")!
let row = Research(id: 0, name: "", date: "", aux: aux, imagen1: image1, imagen2: image2, imagen3: image3, imagen4: image4, imagen5: image5, imagen6: image6)
dataResearch.append(row)
aux = aux + 1
}
}
IMAGE PICKER
import UIKit
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
var sourceType: UIImagePickerController.SourceType = .photoLibrary
#Binding var selectedImage: UIImage
//#Binding var selectedImage: SelectImage
#Environment(\.presentationMode) private var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = false
imagePicker.sourceType = sourceType
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
print("aqui vemos-- ", parent)
print("aqui vemos-- ", parent.selectedImage)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
parent.selectedImage = image
//var row1 = SelectImage(id: 0, uiImage: image ,name:"")
//parent.selectedImage.uiImage = image
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}
This was a little bit complicated because of the structure of Research and the fact that it has so many image fields (that aren't in an array). I'd suggest that it's possible that your model could use a refactor, but since I don't have any information about what the end goal is, I don't know exactly what that would involve yet.
With your current code, you can set a selected item and then set an additional variable that determines which imagen(X) property it should be bound to:
struct ContentView: View {
#State private var dataResearch : [Research] = []
#State private var aux: Int = 0
#State private var pickerItem : Binding<Research>? // the item from ForEach
private enum SelectedImage {
case imagen1, imagen2
}
#State private var selectedImage : SelectedImage? //which image gets selected
var body: some View {
VStack{
ForEach($dataResearch, id: \.aux) { $i in
VStack{
Image(uiImage: i.imagen1)
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.edgesIgnoringSafeArea(.all)
.onTapGesture(){
pickerItem = $i
selectedImage = .imagen1
}
Image(uiImage: i.imagen2)
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.edgesIgnoringSafeArea(.all)
.onTapGesture(){
pickerItem = $i
selectedImage = .imagen2
}
}
}
.sheet(item: $pickerItem) { [selectedImage] item in
VStack {
if let selectedImage = selectedImage {
switch selectedImage {
case .imagen1:
ImagePicker(selectedImage: item.imagen1)
case .imagen2:
ImagePicker(selectedImage: item.imagen2)
}
}
}
.ignoresSafeArea()
}
}
.onAppear(){
getRequest()
}
}
func getRequest(){
let image1: UIImage = UIImage(systemName: "pencil")!
let image2: UIImage = UIImage(systemName: "pencil")!
let image3: UIImage = UIImage(systemName: "pencil")!
let image4: UIImage = UIImage(systemName: "pencil")!
let image5: UIImage = UIImage(systemName: "pencil")!
let image6: UIImage = UIImage(systemName: "pencil")!
let row = Research(id: 0, name: "", date: "", aux: aux, imagen1: image1, imagen2: image2, imagen3: image3, imagen4: image4, imagen5: image5, imagen6: image6)
dataResearch.append(row)
aux = aux + 1
}
}
Using Swift 2.0 I am hoping to find a way to capture the resized image after the user has selected how they want to see it in the frame from the scroll view (ZoomScrollView).
I know there are complex examples out there from Swift but was hoping to find a simpler way to capture this in Swift 2.0. In all my searching I've heard references to using ZStack and some masks or overlays but can't find a simple good example.
I am hoping someone can update my example with the ZStack, masks, etc and how to extract the image for saving or provide a better example.
import SwiftUI
struct ContentView: View {
#Environment(\.presentationMode) var presentationMode
#State var isAccepted: Bool = false
#State var isShowingImagePicker = false
#State var isShowingActionPicker = false
#State var sourceType:UIImagePickerController.SourceType = .camera
#State var image:UIImage?
var body: some View {
HStack {
Color(UIColor.systemYellow).frame(width: 8)
VStack(alignment: .leading) {
HStack {
Spacer()
VStack {
if image != nil {
ZoomScrollView {
Image(uiImage: image!)
.resizable()
.scaledToFit()
}
.frame(width: 300, height: 300, alignment: .center)
.clipShape(Circle())
} else {
Image(systemName: "person.crop.circle")
.resizable()
.font(.system(size: 32, weight: .light))
.frame(width: 300, height: 300, alignment: .center)
.cornerRadius(180)
.foregroundColor(Color(.systemGray))
.clipShape(Circle())
}
}
Spacer()
}
Spacer()
HStack {
Button(action: {
self.isShowingActionPicker = true
}, label: {
Text("Select Image")
.foregroundColor(.blue)
})
.frame(width: 130)
.actionSheet(isPresented: $isShowingActionPicker, content: {
ActionSheet(title: Text("Select a profile avatar picture"), message: nil, buttons: [
.default(Text("Camera"), action: {
self.isShowingImagePicker = true
self.sourceType = .camera
}),
.default(Text("Photo Library"), action: {
self.isShowingImagePicker = true
self.sourceType = .photoLibrary
}),
.cancel()
])
})
.sheet(isPresented: $isShowingImagePicker) {
imagePicker(image: $image, isShowingImagePicker: $isShowingImagePicker ,sourceType: self.sourceType)
}
Spacer()
// Save button
Button(action: {
// Save Image here... print for now just see if file dimensions are the right size
print("saved: ", image!)
self.presentationMode.wrappedValue.dismiss()
}
) {
HStack {
Text("Save").foregroundColor(isAccepted ? .gray : .blue)
}
}
.frame(width: 102)
.padding(.top)
.padding(.bottom)
//.buttonStyle(RoundedCorners())
.disabled(isAccepted) // Disable if if already isAccepted is true
}
}
Spacer()
Color(UIColor.systemYellow).frame(width: 8)
}
.padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.top)
.background(Color(UIColor.systemYellow))
}
}
struct ZoomScrollView<Content: View>: UIViewRepresentable {
private var content: Content
init(#ViewBuilder content: () -> Content) {
self.content = content()
}
func makeUIView(context: Context) -> UIScrollView {
// set up the UIScrollView
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator // for viewForZooming(in:)
scrollView.maximumZoomScale = 20
scrollView.minimumZoomScale = 1
scrollView.bouncesZoom = true
// create a UIHostingController to hold our SwiftUI content
let hostedView = context.coordinator.hostingController.view!
hostedView.translatesAutoresizingMaskIntoConstraints = true
hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
hostedView.frame = scrollView.bounds
scrollView.addSubview(hostedView)
return scrollView
}
func makeCoordinator() -> Coordinator {
return Coordinator(hostingController: UIHostingController(rootView: self.content))
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
// update the hosting controller's SwiftUI content
context.coordinator.hostingController.rootView = self.content
assert(context.coordinator.hostingController.view.superview == uiView)
}
// MARK: - Coordinator
class Coordinator: NSObject, UIScrollViewDelegate {
var hostingController: UIHostingController<Content>
init(hostingController: UIHostingController<Content>) {
self.hostingController = hostingController
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return hostingController.view
}
}
}
struct imagePicker:UIViewControllerRepresentable {
#Binding var image: UIImage?
#Binding var isShowingImagePicker: Bool
typealias UIViewControllerType = UIImagePickerController
typealias Coordinator = imagePickerCoordinator
var sourceType:UIImagePickerController.SourceType = .camera
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
picker.delegate = context.coordinator
return picker
}
func makeCoordinator() -> imagePickerCoordinator {
return imagePickerCoordinator(image: $image, isShowingImagePicker: $isShowingImagePicker)
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}
class imagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#Binding var image: UIImage?
#Binding var isShowingImagePicker: Bool
init(image:Binding<UIImage?>, isShowingImagePicker: Binding<Bool>) {
_image = image
_isShowingImagePicker = isShowingImagePicker
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let uiimage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
image = uiimage
isShowingImagePicker = false
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isShowingImagePicker = false
}
}
Just want to return the image that's zoomed in the circle. The image can be square (re: the 300x300 frame), that's fine just need the zoomed image not whole screen or the original image.
the following changes were successful based the comments:
Add the following State variables:
#State private var rect: CGRect = .zero
#State private var uiimage: UIImage? = nil // resized image
Added "RectGetter" to the picked image frame after image selected selected
if image != nil {
ZoomScrollView {
Image(uiImage: image!)
.resizable()
.scaledToFit()
}
.frame(width: 300, height: 300, alignment: .center)
.clipShape(Circle())
.background(RectGetter(rect: $rect))
Here is the struct and extension I added
extension UIView {
func asImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
struct RectGetter: View {
#Binding var rect: CGRect
var body: some View {
GeometryReader { proxy in
self.createView(proxy: proxy)
}
}
func createView(proxy: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = proxy.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
Last I set the image to save
self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect)
This assumes the root controller. However, in my production app I had to point to self
self.uiimage = UIApplication.shared.windows[0].self.asImage(rect: self.rect)
Then I was able to save that image.
A couple of notes. The image returned is the rectangle which is fine. However due to the way the image is captured the rest of the rectangle outside the cropShape of a circle has the background color. In this case yellow at the for corners outside the circle. There is probably a way to have some sort of ZOrder mask that overlays the image for display when you are resizing the image but then this accesses the right layer and saves the full rectangle picture. If anyone wants to suggest further that would be a cleaner solution but this works assuming you will always display the picture in the same crop shape it was saved in.
SwiftUI has implicit animations with .animate(), and explicit ones using .withAnimation(). However, I can't figure out how to animate an image change:
struct ImageViewWidget : View {
#ObjectBinding var imageLoader: ImageLoader
init(imageURL: URL) {
imageLoader = ImageLoader(imageURL: imageURL)
}
var body: some View {
Image(uiImage:
(imageLoader.data.count == 0) ? UIImage(named: "logo-old")! : UIImage(data: imageLoader.data)!)
.resizable()
.cornerRadius(5)
.frame(width: 120, height:120)
}
}
This Image's uiImage argument is passed the old-logo (placeholder) if there's no data in imageLoader (a BindableObject), and replaces it with the correct one once that's asynchronously loaded:
class ImageLoader : BindableObject {
let didChange = PassthroughSubject<Data, Never>()
var data = Data() {
didSet {
didChange.send(data)
}
}
init(imageURL: URL) {
print("Image loader being initted!")
let url = imageURL
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
DispatchQueue.main.async {
self.data = data
}
}.resume()
}
}
How can I animate this change, the moment where data.count stops being 0, and we have the image? say I want a fade out-in animation..
If you want to use explicit animations based on environment objects (or observable objects), you need to create some state in your view.
You can react to changes to an observable in your view using onReceive, and then modify your state using explicit animation.
struct ImageViewWidget: View {
#ObservedObject var imageLoader: ImageLoader
#State var uiImage: UIImage = UIImage(named: "logo-old")!
init(imageURL: URL) {
imageLoader = ImageLoader(imageURL: imageURL)
}
var body: some View {
Image(uiImage: uiImage)
.resizable()
.cornerRadius(5)
.frame(width: 120, height: 120)
.onReceive(imageLoader.$data) { data in
if data.count != 0 {
withAnimation {
self.uiImage = UIImage(data: data)!
}
}
}
}
}
You don't necessarily have to call .animate() or .withAnimation() because you are simply switching the images, you can use .transition() instead.
Assuming you have already successfully updated your image with your #ObjectBinding(#ObservedObject in Beta5), you can do this:
var body: some View {
if imageLoader.data.count == 0 {
Image(uiImage: UIImage(named: "logo-old")!)
.resizable()
.cornerRadius(5)
.frame(width: 120, height:120)
.transition(.opacity)
.animation(.easeInOut(duration:1))
} else {
Image(uiImage: UIImage(data: imageLoader.data)!)
.resizable()
.cornerRadius(5)
.frame(width: 120, height:120)
.transition(.opacity)
.animation(.easeInOut(duration:1))
}
}
or you can use a custom view modifier if you want to make the transition fancier:
struct ScaleAndFade: ViewModifier {
/// True when the transition is active.
var isEnabled: Bool
// fade the content view while transitioning in and
// out of the container.
func body(content: Content) -> some View {
return content
.scaleEffect(isEnabled ? 0.1 : 1)
.opacity(isEnabled ? 0 : 1)
//any other properties you want to transition
}
}
extension AnyTransition {
static let scaleAndFade = AnyTransition.modifier(
active: ScaleAndFade(isEnabled: true),
identity: ScaleAndFade(isEnabled: false))
}
and then inside your ImageViewWidget, add .transition(.scaleAndFade) to your Image as its view modifier