How do I get the dimensions of an Image? - swiftui

I'm making an app with SwiftUI. I have an Image that has been loaded by the user. I want to show its dimensions in the UI. How do I get these dimensions from this image which is held inside this Image?
To be clear, I don't mean the view's size on-screen. I mean the number of pixels wide and high, which is encoded in the image file/format (PNG, JPG, etc). Before it's been scaled on the screen or anything. The conceptual image's dimensions.

You can load the Image using the uiImage initialiser. By having the image loaded through UIImage, you get access to its size:
struct ContentView : View {
#State private var img = UIImage(named: "myimage") ?? UIImage()
var body: some View {
VStack {
Image(uiImage: img)
Text("\(img.size.width) x \(img.size.height)")
}
}
}

Related

SwiftUI: Overlay an SKScene over an SCNScene

Setup:
For displaying 3D elements Scenekit is used. For 2D elements Spritekit.
SceneView() is used to display a scene.
An SCNScene and an SCNNode for the camera are given.
Goal:
Now I want to put a 2D overlay over the SCNScene. Previously this could be achieved using the overlaySKScene property of the SCNView. But now that in SwiftUI only the scene and camera are given I wonder how this can be achieved.
Thanks already in advance.
The SpriteKit overlay is not supported by SceneView in iOS 14.
If you want a SpriteKit scene layered on top of an SCNScene, you can do it with SwiftUI, like this:
struct MyView: View {
#State private var 2dscene = MyScene()
#State private var 3dScene = SCNScene(named: "MyFile")
var body: some View {
ZStack {
SceneView(scene: 3dScene)
SpriteView(scene: 2dscene)
}
}
}

SwiftUI Image not switching to dark mode when created using UIImage

I have a simple SwiftUI view where I create a Image view using a UIImage. The issue is that the Image created with the UIImage is not switching to the dark or light mode when the color scheme changes. It works correctly only if the entire view is reloaded. It works as expected if I use create the image directly with the image name.
struct ContentView: View {
var body: some View {
HStack {
VStack {
Image("my-image")
Text("Image")
}
VStack {
Image(uiImage: UIImage(named: "my-image")!)
Text("Image+uiImage")
}
}
}
}
The image itself is in a xcasset catalog
result is this (note the right image not changing when the appearance changes but only after restarting the app)
This happens when the SwiftUI Image is initialized using an UIImage (seems to be a static initialization with no further observation of the original UIImage).
Workaround: The view can be informed to redraw manually, when using the colorScheme environment variable. Even when it seems to be unused in your later code.
This is pretty useful, if you need to consume UIImages from a shared UIKit/SwiftUI codebase.
In your example:
struct ContentView: View {
// This is required for the automatic re-creation of the view
// (and thus updating the Image with the appropriate appearance
// for the UIImage)
#Environment(\.colorScheme) private var colorScheme // <- view get's notified
var body: some View {
HStack {
VStack {
Image("my-image")
Text("Image")
}
VStack {
Image(uiImage: UIImage(named: "my-image")!)
Text("Image+uiImage")
}
}
}
}
Be aware that this might lead to unneeded redrawing, so try to use SwiftUI Images first, as #Jan said.
The workaround here is to use Image without using the UIImage constructor

iOS WidgetKit: remote images fails to load

I'm observing a bizarre thing: in the new widgets far too often remote images are not being displayed even though the image has been successfully loaded and placed in cache.
For image downloading I've tried:
SDWebImageSwiftUI
Kingfisher
SwURL
All of them indicate that image loading succeed, but the actual widget is not showing it.
struct TestWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
WebImage(url: URL(string: "https://miro.medium.com/max/3840/0*TLqp5Uwavd-U_xrs.jpg"))
.onSuccess()
.resizable()
}
}
On the second run of the debugger - with image loading from cache - I get the image displayed, but never(?) on initial run.
It feels like that in onSuccess I need to trigger UI-invalidation? But how?
(Since it happens to literally every image-lib I try - I don't think that it's something off in the libs)
Environment:
iOS 14 Beta 3 (both device and simulators)
Xcode 12 Beta 3
During the debug run, memory use is around 15mb
Yes, as mentioned by Konstantin, it is not supported to load images asynchronously. There are 2 options to load network image in widgets
Either fetch all the images in TimelineProvider and inject them to the views directly.
OR
Use Data(contentsOf: url) to fetch the image. This way it still fetches them synchronously but the code is cleaner. Sample code -
struct NetworkImage: View {
private let url: URL?
var body: some View {
Group {
if let url = url, let imageData = try? Data(contentsOf: url),
let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fill)
}
else {
Image("placeholder-image")
}
}
}
}
This view can simply be use like this -
NetworkImage(url: url)
Got it: it's simply not supported and you aim to load images inside TimelineProvider:
https://developer.apple.com/forums/thread/652581

How to get frames (size and coordinates) for different views in SwiftUI?

I need to get frames for multiple views inside a VStack. I think using the GeometryReader but it returns the necessary parameters only for the parent. How can I get these parameters for each view on the screenshot below?
We can use the GeometryReader inside the overlay on needed views and store necessary frames in an array or as separate variables.
ForEach(0..<n) { number in
YourView()
.overlay(
GeometryReader { geometry in
Color.clear
.onAppear {
self.buttonFrames[number] = geometry.frame(in: .global)
}
}
)
}
For more details, you can check this source: https://github.com/twostraws/SwiftOnSundays/blob/master/020%20Switcharoo/Project/Switcharoo/ContentView.swift
and the full tutorial: https://www.youtube.com/watch?v=ffV_fYcFoX0

Trying to update the background image of a UIButton on click

I have a button with a set background image. It's a png with most of the image being transparent. When that image is tapped on (selected), I would like to update the background image of the button to a different image. It seems to be working, but it's not removing the original background image. It seems to be placing the new background image behind the old one. So the transparent background image seems to be above the selected background image.
Setting the background image:
owedTotalBtn.setBackgroundImage( UIImage.init(named:"total-owed"), for: .normal)
Updating the background image one the button is tapped:
#IBAction func showOwed(_ sender: Any) {
owedTotalBtn.setBackgroundImage( UIImage.init(named:"total-owed-selected"), for: .normal)
}
You have set background Image for both normmal state. Try this code:
owedTotalBtn.setBackgroundImage( UIImage.init(named:"total-owed"), for: .normal)
owedTotalBtn.setBackgroundImage( UIImage.init(named:"total-owed"), for: .selected)
#IBAction func showOwed(_ sender: Any) {
sender.isSelected = !sender.isSelected
}