My code converts the output .WEBP image from an URL on user's screen, and saves it to iOS photo library when the user clicks the download button.
The download button opens up the share sheet, and user clicks "save image" and it fails while saving after user gives its consent to access the photo library.
It does not fail on the simulator, but when I click the save image, it does not show any image in the photo library, because the image after .Webp conversion returns an empty file.
import SwiftUI
import SDWebImageSwiftUI
import UIKit
extension UIImageView{
func imageFrom(url:URL) -> UIImage {
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url){
if let image = UIImage(data:data){
DispatchQueue.main.async{
self?.image = image
}
}
}
}
return image!
}
}
extension String {
func load() -> UIImage {
do {
guard let url = URL(string: self)
else {
return UIImage()
}
let data: Data = try Data(contentsOf:
} catch {URL)
return UIImage(data:
}
return UIImage()
}
}
struct ResultView: View {
#State var chocolate = chocolate.shared
#ObservedObject var imagesModel = ImagesModel.shared
#ObservedObject var appState = AppState.shared
#State var imageSaver = ImageSaver.shared
#State var sharingImageItems: [Any] = []
#State var sharingImage: UIImage = UIImage()
#State var isShareSheetShowing:Bool = false
#Binding var presentedResultView: Bool
func shareButton() {
isShareSheetShowing.toggle()
}
var body: some View {
VStack {
HStack {
Text("Result")
.font(.system(size: 20, weight: .bold))
.foregroundColor(.black)
Spacer()
Button {
appState.presentResult = false
} label: {
Image("Glyph")
}
}
.padding(20)
Image(uiImage: imagesModel.imageUrl.load())
.resizable()
.scaledToFit()
.cornerRadius(6)
Image(uiImage: sharingImage)
Text("\(ContentView().promptField)")
.font(.system(size: 16))
.foregroundColor(.textColor)
Spacer()
VStack {
HStack{
Button("Download"){
sharingImageItems.removeAll()
sharingImageItems.append(sharingImage)
isShareSheetShowing.toggle()
}
.frame(width: 150, height: 45)
.font(Font.system(size:18, weight: .bold))
.foregroundColor(Color.white)
.background(Color.black)
.cornerRadius(6)
}
}
.onAppear() {
sharingImage = imageSaver.saveImage(imageUrl: imagesModel.imageUrl) }
}
.sheet(isPresented: $isShareSheetShowing, content: {
ShareSheet(items: sharingImageItems)
})
.frame(maxWidth: .infinity, maxHeight: .infinity )
.padding(20)
.background(Color.white)
}
}
struct ShareSheet : UIViewControllerRepresentable {
var items: [Any]
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(activityItems: items, applicationActivities: nil)
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
}
}
data) ?? UIImage()
Related
So I am working on users uploading images to a "profile picture" of sorts. So my image picker I believe is working fine. The issue is it uploads, but the default image still shows initially.
I know this because if I go to upload another image, for a brief moment the default picture disappears and the new uploaded image can be seen. My button triggers the showingImagePicker bool which shows the new image and should hide the default image, I don't understand why the default image would still be showing.
struct RecipeEditorImage: View {
#State private var showingImagePicker = false
#State private var inputImage: UIImage?
#State private var image: Image?
var body: some View {
ZStack (alignment: .trailing){
if showingImagePicker{
image?
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:200, height: 100)
}
else{
Image("ExampleRecipePicture")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:200, height: 100)
}
Image(systemName:("plus.circle.fill")).renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fill)
.offset(x: 20)
.foregroundColor(Color("completeGreen"))
.frame(width:50, height:10)
.onTapGesture {
showingImagePicker = true
}
.sheet(isPresented: $showingImagePicker){
EditorImagePicker(image: $inputImage)
}
.onChange(of: inputImage){ _ in loadImage() }
}
}
func loadImage(){
guard let inputImage = inputImage else { return }
image = Image(uiImage: inputImage)
}
}
import PhotosUI
import SwiftUI
struct EditorImagePicker: UIViewControllerRepresentable{
#Binding var image: UIImage?
class Coordinator: NSObject, PHPickerViewControllerDelegate{
var parent: EditorImagePicker
init(_ parent: EditorImagePicker){
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider else { return }
if provider.canLoadObject(ofClass: UIImage.self){
provider.loadObject(ofClass: UIImage.self){image, _ in
self.parent.image = image as? UIImage
}
}
}
}
func makeUIViewController(context: Context) -> PHPickerViewController {
//configures ios to just be able to select images
var config = PHPickerConfiguration()
config.filter = .images
//the view of picker
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
//leave empty for now
}
}
There are a few issues:
Your first if clause guarantees the chosen image will only be shown if the sheet is shown as well.
You probably want to avoid the extra step of storing a reference to the Image View -- just use it dynamically. That gets rid of the onChange as well.
Your loadObject closure needs to call back to the main thread.
import SwiftUI
import PhotosUI
struct RecipeEditorImage: View {
#State private var showingImagePicker = false
#State private var inputImage: UIImage?
var body: some View {
ZStack (alignment: .trailing){
if let inputImage = inputImage {
Image(uiImage: inputImage)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:200, height: 100)
} else{
Image(systemName: "pencil")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:200, height: 100)
}
Image(systemName:("plus.circle.fill")).renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fill)
.offset(x: 20)
.foregroundColor(Color("completeGreen"))
.frame(width:50, height:10)
.contentShape(Rectangle())
.onTapGesture {
showingImagePicker = true
}
.sheet(isPresented: $showingImagePicker){
EditorImagePicker(image: $inputImage)
}
}
}
}
struct EditorImagePicker: UIViewControllerRepresentable{
#Binding var image: UIImage?
class Coordinator: NSObject, PHPickerViewControllerDelegate{
var parent: EditorImagePicker
init(_ parent: EditorImagePicker){
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider else { return }
if provider.canLoadObject(ofClass: UIImage.self){
provider.loadObject(ofClass: UIImage.self){image, _ in
DispatchQueue.main.async {
self.parent.image = image as? UIImage
}
}
}
}
}
func makeUIViewController(context: Context) -> PHPickerViewController {
//configures ios to just be able to select images
var config = PHPickerConfiguration()
config.filter = .images
//the view of picker
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
//leave empty for now
}
}
I need the image picker to set a binding variable to True after an image is selected. After that image is selected, it needs to be saved. But after I select the image, the views changes. It isn't until I kill the app that the image and variable are changed back.
For the image picker, An error comes out "Instance member 'TrueBadge' of type 'PhotoPicker' cannot be used on instance of nested type 'PhotoPicker.Coordinator'"
Here's BadgeScreen View:
import Foundation
var PresentedBadge = UIImage(systemName: "questionmark")!
func loadImage() -> UIImage {
do {
if let furl = fileURL {
let data = try Data(contentsOf: furl)
if let img = UIImage(data: data) {
return img
}
}
} catch {
print("error: \(error)") // todo
}
return UIImage()
}
var IsDone = false
struct Badge: View {
#Binding var TrueBadge: Bool //Need help switching this Binding to true
#State private var ComplianceBadgeIsPicking = UIImage(named: "BlankComplianceBadge")!
#State private var isShwoingPhotoPicker = false
#State private var ShowInstruction = false
#State private var AlertToReplaceBade = false
var body: some View {
//The beginning
if TrueBadge {
Color("MainBadgeScreen")
.edgesIgnoringSafeArea(.all)
.overlay(
VStack{
Text("Clearance Status")
.font(.title)
.fontWeight(.semibold)
.offset(y: -15)
.foregroundColor(.white)
Text("Vaccine Compliant")
.foregroundColor(.white)
.bold()
.font(.system(size: 30))
Image(uiImage: ContentViewBadge)
.resizable()
.aspectRatio(contentMode: .fit)
.scaledToFit()
Button(action: {
AlertToReplaceBade.toggle()
}) {
Image(systemName: "trash" )
Text("Remove")
}
.foregroundColor(.white)
.padding()
.background(Color.red)
.cornerRadius(15)
.offset(y: 13)
}.alert(isPresented: $AlertToReplaceBade, content: {
Alert(title: Text("Are you sure you would like to remove your current badge?"),
message: Text("Remeber that this badge is and will be permanently removed"),
primaryButton: .default(Text("Yes"), action: {
// Somehow need to remove the image and activate the UIImagePickerController
}), secondaryButton: .cancel(Text("No, I do not")))
})
)}
else {
Color("ExpiredBadgeScreen")
.edgesIgnoringSafeArea(.all)
.overlay(
VStack{
Image(systemName: "person.crop.circle.badge.questionmark.fill")
.font(.system(size:150))
.offset(y: -10)
.foregroundColor(.black)
Text("Compliance Badge")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.black)
.offset(y: -2)
Text("You do not have a current vaccine compliant badge. Please upload one that shows you are vaccine compliant or within 'green' status")
.font(.system(size: 15))
.foregroundColor(.black)
.fontWeight(.bold)
.multilineTextAlignment(.center)
.frame(width: 270, height: 140, alignment: .center)
.offset(y: -26)
Button(action: {
ShowInstruction.toggle()
}) {
Image(systemName: "questionmark.circle")
Text("How to upload")
.bold()
.font(.system(size:20))
}
.offset(y: -40)
Button(action: {
isShwoingPhotoPicker.toggle()
}) {
Image(systemName: "square.and.arrow.up")
Text("Upload Badge")
.bold()
.font(.system(size:20))
}
.offset(y: -10)
}.sheet(isPresented: $ShowInstruction, content: {
Instruction()
})
.sheet(isPresented: $isShwoingPhotoPicker, content: {
PhotoPicker(TrueBadge: $TrueBadge, Badge: $ComplianceBadgeIsPicking)
})
.accentColor(.black)
)
}
//The End
}
}
var ContentViewBadge = UIImage(systemName: "questionmark")!
var fileURL: URL?
func saveImage() {
do {
let furl = try FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("imageFile")
.appendingPathExtension("png")
fileURL = furl
try ContentViewBadge.pngData()?.write(to: furl)
} catch {
print("could not create imageFile")
}
}
struct PhotoPicker: UIViewControllerRepresentable {
//Since the Binding cannot be created above, I have no choice but to put it here
#Binding var TrueBadge: Bool
#Binding var Badge: UIImage
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.allowsEditing = true
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator(photoPicker: self)
}
final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let photoPicker: PhotoPicker
init(photoPicker: PhotoPicker){
self.photoPicker = photoPicker
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.editedImage] as? UIImage{
photoPicker.Badge = image
ContentViewBadge = photoPicker.Badge
// This is where I am having trouble at. I am unable to change the Bool type of "TrueBadge" to true and have it stay on even after the application
// Is killed
// I also need the selected image to remain here. If i kill the application, the image is removed and the Boolean and set to false
//Error is being created below
TrueBadge = true
}
picker.dismiss(animated: true)
}
}
}```
And here's MainScreen view:
```import SwiftUI
import SafariServices
struct ContentView: View {
#State var ShowInstruction = false
#State var ShowBadge = false
#State var ShowPortal = false
#State var ShowDetails = false
#State var ViewAlert = false
#State var TrueBadge = false
var body: some View {
NavigationView{
Color("BackgroundMain")
.edgesIgnoringSafeArea(.all)
.overlay(
VStack{
//Need the if Bool condition to set to true after an image is safe and the variable is set to true
if TrueBadge {
Button(action: {
ShowBadge.toggle()
}) {
Image(systemName: "doc.text.image")
.font(.system(.largeTitle))
.foregroundColor(.white)
.frame(width: 200, height: 100, alignment: .center)
.background(Color("MainBadgeScreen"))
.cornerRadius(15)
}
}
else {
Button(action: {
self.ViewAlert = true
}) {
Image(systemName: "doc.text.image")
.font(.system(.largeTitle))
.foregroundColor(.white)
.frame(width: 200, height: 100, alignment: .center)
.background(Color("ExpiredBadgeScreen"))
.cornerRadius(15)
}
}
}
.alert(isPresented: $ViewAlert, content: {
Alert(title: Text("You agree to use this app responsibly?"), primaryButton: .default(Text("Yes"), action: {
ShowBadge = true
}), secondaryButton: .cancel(Text("No, I do not")))
}))
.sheet(isPresented: $ShowBadge, content: {
Badge(TrueBadge: $TrueBadge)
})
.navigationTitle("Home")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Button(action: {
ShowPortal = true
}) {
Text("Student Health Portal")
Image(systemName: "heart.fill")
}
Button(action: {
ShowInstruction = true
}) {
Text("Instructions")
Image(systemName: "questionmark.circle")
}
Button(action: {
ShowDetails = true
}) {
Image(systemName: "info.circle")
Text("About")
}
}
label: {
icon: do {
Image(systemName: "gearshape.fill")
.foregroundColor(Color("WithSystem"))
}
}
}
}
.sheet(isPresented: $ShowPortal, content: {
safari()
})
.sheet(isPresented: $ShowInstruction, content: {
Instruction()
})
.sheet(isPresented: $ShowDetails, content: {
Details()
})
}
.accentColor(Color(.label))
}
struct safari : UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<safari>) -> SFSafariViewController{
let controller = SFSafariViewController(url: URL(string: "https://patientportal.bowiestate.edu/login_directory.aspx")!)
return controller
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<safari>) {
}
}
}```
Sorry for beginner question, I am trying to transition from UIKit to SwiftUI. #State variable's didSet does not get triggered like it does in UIKit.
I have KFImage that loads an image from a user's photoUrl and I want it when tapped, launches image picker, then update the userUIImage but since KFImage needs a url to be updated, I am not sure how I can call my updateUserImage() to update the photoUrl to update KFImage.
Here' my code below
struct ProfileView: View {
//MARK: Properties
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
#State private var isImageLoaded: Bool = false
#State private var showImagePicker = false
#State private var photoUrl: URL? = URL(string: (Customer.current?.photoUrl)!)
#State private var userImage: Image? = Image(uiImage: UIImage())
#State private var userUIImage: UIImage? = UIImage() {
didSet {
if isImageLoaded {
updateUserImage()
}
}
}
var body: some View {
KFImage(photoUrl)
.resizable()
.onSuccess { result in
self.userUIImage = result.image
self.isImageLoaded = true
}
.aspectRatio(contentMode: .fill)
.frame(width: screenWidth / 2.5)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
.onTapGesture { self.showImagePicker = true }
.sheet(isPresented: $showImagePicker) {
CustomImagePickerView(sourceType: .photoLibrary, image: $userImage, uiImage: $userUIImage, isPresented: $showImagePicker)
}
}
//MARK: Methods
func updateUserImage() {
CustomerService.updateUserImage(image: userUIImage!) { (photoUrl, error) in
if let error = error {
handleError(errorBody: error)
return
}
guard var user = Customer.current else { return }
self.photoUrl = photoUrl
user.photoUrl = photoUrl?.absoluteString
CustomerService.updateUserDatabase(user: user) { (error) in
if let error = error {
handleError(errorBody: error)
return
}
Customer.setCurrent(user, writeToUserDefaults: true)
}
}
}
}
Try using onChange / onReceive instead:
KFImage(photoUrl)
.resizable()
// ...
// remove .onSuccess implementation as self.userUIImage = result.image will result in an infinite loop
.onChange(of: userUIImage) { newImage in
updateUserImage()
}
or
import Combine
...
KFImage(photoUrl)
.resizable()
// ...
.onReceive(Just(userUIImage)) { newImage in
updateUserImage()
}
I am trying to take a screenshot of the selected area using CGRect. It works fine if I don't use #State variable. But I need to use #State variable too.
Here is my code...
struct ScreenShotTest: View {
#State var abc = 0 //Works well if I remove the line
var body: some View {
Button(action: {
let image = self.takeScreenshot(theRect: CGRect(x: 0, y: 0, width: 200, height: 100))
print(image)
}) {
Text("Take Screenshot")
.padding(.all, 10)
.background(Color.blue)
.foregroundColor(.white)
}
}
}
extension UIView {
var renderedImage: UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale)
let context: CGContext = UIGraphicsGetCurrentContext()!
self.layer.render(in: context)
let capturedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return capturedImage
}
}
extension View {
func takeScreenshot(theRect: CGRect) -> UIImage {
let window = UIWindow(frame: theRect)
let hosting = UIHostingController(rootView: self)
hosting.view.frame = window.frame
window.addSubview(hosting.view)
window.makeKeyAndVisible()
return hosting.view.renderedImage
}
}
I ran into this problem too, but with an EnvironmentObject. I solved it by declaring the EnvironmentObject in an outer view and passing it as a plain variable to the inner view that was crashing when I used it. This is pared way down from my real code, but it should convey the idea.
struct Sample: View {
#State private var isSharePresented: Bool = false
#EnvironmentObject var model: Model
static var screenshot: UIImage?
var body: some View {
GeometryReader { geometry in
// Hack to get a black background.
ZStack {
Spacer()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.black)
.edgesIgnoringSafeArea(.all)
VStack {
CompletionContent(model: Model.theModel)
Spacer()
Button(action: {
model.advanceToNext()
}) {
Text("Continue", comment: "Dismiss this view")
.font(.headline)
.foregroundColor(.init(UIColor.link))
}
HStack {
Spacer()
Button(action: {
Sample.screenshot = self.takeScreenshot(theRect: (geometry.frame(in: .global)))
self.isSharePresented = true
}) {
VStack {
Image("blank")
Image(systemName: "square.and.arrow.up")
.resizable()
.frame(width: 20, height: 30)
}
}
}
Spacer()
}
}
}
.sheet(isPresented: $isSharePresented, onDismiss: {
print("Dismiss")
}, content: {
ActivityViewController(screenshot: Sample.screenshot!)
})
}
}
struct SampleContent : View {
var model: Model
var body: some View {
Text("Content that uses the model variable goes here")
}
}
extension UIView {
var renderedImage: UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale)
let context: CGContext = UIGraphicsGetCurrentContext()!
self.layer.render(in: context)
let capturedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return capturedImage
}
}
extension View {
func takeScreenshot(theRect: CGRect) -> UIImage {
let window = UIWindow(frame: theRect)
let hosting = UIHostingController(rootView: self)
hosting.view.frame = window.frame
window.addSubview(hosting.view)
window.makeKeyAndVisible()
return hosting.view.renderedImage
}
}
struct ActivityViewController: UIViewControllerRepresentable {
var screenshot: UIImage
var applicationActivities: [UIActivity]? = nil
func makeUIViewController(
context: UIViewControllerRepresentableContext<ActivityViewController>)
-> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: [screenshot], applicationActivities: applicationActivities)
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityViewController>) {
}
}
I have tried this, but I didn't know how to use the results in a SwiftUI View:
func getProfilePicture(_ completion: #escaping ((UIImage) -> Void)) {
Alamofire.request(GIDSignIn.sharedInstance()?.currentUser.profile.imageURL(withDimension: 75) ?? "https://httpbin.org/image/png").responseImage { response in
if let image = response.result.value {
completion(image)
}
}
}
If you can help, I would like to put the returned image from the completion handler in this view:
struct ProfileView: View {
let profileInfo = ProfileInfo()
var placeHolderImage = Image(systemName: "person")
var body: some View {
Group {
placeHolderImage
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
.padding(10)
}
}
}
I would like this to return a UIImage so I can eventually use it in a SwiftUI view. I have already tried using a method with an #escaping completion handler, but I couldn't figure out how to use it to fix the issue. Thanks!
You can try something let this:
struct ProfileView: View {
#State var placeHolderImage = Image(systemName: "person")
var body: some View {
Group {
placeHolderImage
.clipShape(Circle())
.overlay(
Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
.padding(10)
}.onAppear{
getProfilePicture{ image in
self.placeHolderImage = Image(uiImage: image)
}
}
}
}
When ProfileView appears it will call getProfilePicture. The image specified in image in (when calling the function) is what the completion handler passes through (completion(image)). What you can then do is change your placeHolderImage to what you get in getProfilePicture, but before you do that you need to make your uiImage into an Image. Also make sure you add the #State keyword to your variable so once it changes your View is updated.
Hope that helps!
Use a completion handler as below,
func getProfilePicture(_ completion: #escaping ((UIImage) -> Void)) {
Alamofire.request(GIDSignIn.sharedInstance()?.currentUser.profile.imageURL(withDimension: 75) ?? "https://httpbin.org/image/png").responseImage { response in
if let image = response.result.value {
completion(image)
}
}
}
import SwiftUI
#available(macCatalyst 14.0, *)
#available(iOS 14.0, *)
struct MyUIView : View {
// MARK: - Properties -
#StateObject var myStore : MyStore = MyStore()
#State var imgArray : Array<Image> = Array<Image>.init(repeating: Image("no-image"), count: 100)
// MARK: - View -
var body : some View {
ScrollView {
Text("IMAGES")
LazyVStack (alignment: .leading, spacing: 8.0) {
// actionPlans
ForEach((0 ..< myStore.data.count).clamped(to: 0..<2), id: \.self) { i in
HStack {
let url = myStore.data[i]
let img : Image = imgArray[i]
img
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .leading)
.padding(EdgeInsets(top: 0, leading: 0.0, bottom: 0, trailing: 16.0))
.onAppear(){
imageFrom(url: url, completion: { image in
imgArray[i] = Image(uiImage: image!)
})
}
}
Divider()
}
}
}.onAppear(perform: {
fetch()
})
}
// MARK: - Media -
private func fetch() {
DispatchQueue.main.async {
myStore.getData()
}
}
func imageFrom(url: String, completion: #escaping (UIImage?) -> Void) {
// image
if ImageLoader.sharedInstance.checkForImage(url: url as NSString?) {
// has image
completion(ImageLoader.sharedInstance.returnImage(url: url as NSString?)!)
} else {
ImageLoader.sharedInstance.getImage(url: url as NSString?) { (image) in
if image != nil {
completion(image)
} else {
completion(UIImage.init(named: "no-image"))
}
}
}
}
}
#available(macCatalyst 14.0, *)
#available(iOS 14.0, *)
struct MyUIView_Previews: PreviewProvider {
static var previews: some View {
MyUIView()
}
}