How to Display Image from a Url in Swiftui

How to display Image from a url in SwiftUI


iOS 15 update:

you can use asyncImage in this way:
AsyncImage(url: URL(string: "https://your_image_url_address"))

more info on Apple developers document:
AsyncImage

Using ObservableObject (Before iOS 15)

first you need to fetch image from url :

class ImageLoader: ObservableObject {
var didChange = PassthroughSubject<Data, Never>()
var data = Data() {
didSet {
didChange.send(data)
}
}

init(urlString:String) {
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
self.data = data
}
}
task.resume()
}
}

you can put this as a part of your Webservice class function too.

then in your ContentView struct you can set @State image in this way :

struct ImageView: View {
@ObservedObject var imageLoader:ImageLoader
@State var image:UIImage = UIImage()

init(withURL url:String) {
imageLoader = ImageLoader(urlString:url)
}

var body: some View {

Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width:100, height:100)
.onReceive(imageLoader.didChange) { data in
self.image = UIImage(data: data) ?? UIImage()
}
}
}

Also, this tutorial is a good reference if you need more

SwiftUI Image from URL not showing

Try using @Published - then you don't need a custom PassthroughSubject:

class ImageLoader: ObservableObject {
// var didChange = PassthroughSubject<Data, Never>() <- remove this
@Published var data: Data?
...
}

and use it in your view:

struct ImageView: View {
var urlString: String
@ObservedObject var imageLoader = ImageLoader()
@State var image = UIImage(named: "homelessDogsCats")!

var body: some View {
ZStack() {
Image(uiImage: image)
.resizable()
.onReceive(imageLoader.$data) { data in
guard let data = data else { return }
self.image = UIImage(data: data) ?? UIImage()
}
}.onAppear {
self.imageLoader.loadData(from: urlString)
}
}
}

Note: if you're using SwiftUI 2, you can use @StateObject instead of @ObservedObject and onChange instead of onReceive.

struct ImageView: View {
var urlString: String
@StateObject var imageLoader = ImageLoader()
@State var image = UIImage(named: "homelessDogsCats")!

var body: some View {
ZStack() {
Image(uiImage: image)
.resizable()
.onChange(of: imageLoader.data) { data in
guard let data = data else { return }
self.image = UIImage(data: data) ?? UIImage()
}
}.onAppear {
self.imageLoader.loadData(from: urlString)
}
}
}

How to display image from known image path - SwiftUI

You say NSOpenPanel, so I'm making going to make the assumption here that we don't need to worry about waiting to load the image over a network:

if let nsImage = NSImage(contentsOf: url) {
Image(nsImage: nsImage)
}

How to display an image in SwiftUI protected by header-based authentication

You just need replace AsyncImage component with your own component. I created this component, is similar to AsyncImage but this receives a custom async function to get the image

struct CustomAsyncImage<Content: View, Placeholder: View>: View {

@State var uiImage: UIImage?

let getImage: () async -> UIImage?
let content: (Image) -> Content
let placeholder: () -> Placeholder

init(
getImage: @escaping () async -> UIImage?,
@ViewBuilder content: @escaping (Image) -> Content,
@ViewBuilder placeholder: @escaping () -> Placeholder
){
self.getImage = getImage
self.content = content
self.placeholder = placeholder
}

var body: some View {
if let uiImage = uiImage {
content(Image(uiImage: uiImage))
}else {
placeholder()
.task {
self.uiImage = await getImage()
}
}
}
}

This is an example of use.

CustomAsyncImage(getImage: getImage) { image in
image
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
} placeholder: {
Text("PlaceHolder")
}

Where getImage is an async function that return an UIImage, you can make that function as you want.

I gonna show you my function as example.

func getImage() async -> UIImage? {
guard let url = URL(string: BASE_URL + "cards/5fd36d31937b6b32d47ad9db/attachments/5fd36d31937b6b32d47ad9de/download") else {
fatalError("Missing URL")
}

var request = URLRequest(url: url)
request.addValue("OAuth oauth_consumer_key=\"\(API_KEY)\", oauth_token=\"\(ACCOUNT_TOKEN)\"", forHTTPHeaderField: "Authorization")

do {
let (data, response) = try await URLSession.shared.data(for: request)

guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching attchment") }

return UIImage(data: data)
} catch {
return nil
}
}

Loading/Downloading image from URL on Swift

Xcode 8 or later • Swift 3 or later

Synchronously:

if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
imageView.contentMode = .scaleAspectFit
imageView.image = image
}

Asynchronously:

Create a method with a completion handler to get the image data from your url

func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

Create a method to download the image (start the task)

func downloadImage(from url: URL) {
print("Download Started")
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
// always update the UI from the main thread
DispatchQueue.main.async() { [weak self] in
self?.imageView.image = UIImage(data: data)
}
}
}

Usage:

override func viewDidLoad() {
super.viewDidLoad()
print("Begin of code")
let url = URL(string: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")!
downloadImage(from: url)
print("End of code. The image will continue downloading in the background and it will be loaded when it ends.")
}

Extension:

extension UIImageView {
func downloaded(from url: URL, contentMode mode: ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: ContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloaded(from: url, contentMode: mode)
}
}

Usage:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")

How can i return images from assets instead of systemName Image and call it in the body Using swiftui

You can do it with an Image Set, an @ViewBuilder.

struct CustomImageView: View {
var body: some View {
Symbols.info.image()
}
enum Symbols: String, CaseIterable{
case info
case success
case warning
case error
var sfSymbolName: String{
switch self {
case .info:
return "info.circle"
case .success:
return "checkmark.seal"
case .warning:
return "exclamationmark.octagon"
case .error:
return "xmark.octagon"
}
}
///Name must exactly match an `Image Set` in `Assets`
///Project Navigator > Assets > + > Image Set >
var assetsName: String{
switch self {
case .info:
return "NameFromAssetsImageSet_info"
case .success:
return "NameFromAssetsImageSet_success"
case .warning:
return "NameFromAssetsImageSet_warning"
case .error:
return "NameFromAssetsImageSet_error"
}
}

@ViewBuilder func image() -> some View {
let showFromAssets: Bool = true
switch showFromAssets{
case true:
Image(assetsName)
case false:
Image(systemName: sfSymbolName)
}
}
}
}


Related Topics



Leave a reply



Submit