Display gif inside NSImageView - Swift 3 - Mac - swift

I am trying to load and display a gif file inside a NSImageView but nothing appears when I run the project.
I have try to link the gif file with NSImageView using url, data or named, but it does not work.
func applicationDidFinishLaunching(_ aNotification: Notification)
{
let url = Bundle.main.url(forResource: "RSynchro-Gif", withExtension: "gif")!
let data = try! Data(contentsOf: url)
gifBoutton.image = NSImage(data: data)
}
or
func applicationDidFinishLaunching(_ aNotification: Notification)
{
let url = Bundle.main.url(forResource: "RSynchro-Gif", withExtension: "gif")!
gifBoutton.image = try! NSImage(data: Data(contentsOf: url))
}
or
func applicationDidFinishLaunching(_ aNotification: Notification)
{
let icon = NSImage(named: NSImage.Name(rawValue: "RSynchro-Gif"))
imageGif.image = icon
imageGif.animates = true
}
None of them works but if I replace the .gif file with a .png, it works.
Anyone to show me the right way?
Thanks.

(See the edit to my answer below. My initial answer was outdated and therefore wrong.)
I don't believe there is native support for GIF image type in Mac OS. (I know for a fact there isn't in iOS, where I do most of my work these days, and am fairly sure the same holds true for Mac OS. I know Mac OS didn't support GIF as of about 3 years ago.)
EDIT:
Apparently I'm wrong. It looks like you can load the first frame of a GIF image if you put the image in your app's bundle directly rather than into your assets catalog. If you create an image set in your asset catalog and drag a GIF into that it appears to work in Xcode but the image doesn't load. (I tried it for both a Mac app and an iOS app and the results are the same.)

Related

SwiftUI display gif image

The way to display animated gif image in swiftUI
because of Image
Image("fall-leaves")
does not support gifs
answer below
Easiest and fastest way to display gif image in swiftUI - is to use Preview / QuickLook (QL) / QLPreviewView
Quartz available only in macOS 10.4+ https://developer.apple.com/documentation/quartz
import SwiftUI
import Quartz
struct QLImage: NSViewRepresentable {
var url: URL
func makeNSView(context: NSViewRepresentableContext<QLImage>) -> QLPreviewView {
let preview = QLPreviewView(frame: .zero, style: .normal)
preview?.autostarts = true
preview?.previewItem = url as QLPreviewItem
return preview ?? QLPreviewView()
}
func updateNSView(_ nsView: QLPreviewView, context: NSViewRepresentableContext<QLImage>) {
nsView.previewItem = url as QLPreviewItem
}
typealias NSViewType = QLPreviewView
}
I was having a hard time using this in my macOS app because I try to load my gif file from the local assets rather than a URL. After looking around for answers, the best answer seems to be putting the gif file into the project folder rather than into the Assets.xcassets and then load the resource into the URL. Somehow Bundle.main.url(forResource: myFile, withExtension: "gif") will return nil if the file isn't in the main project folder. I checked the ProjectName -> Target -> Build Phases -> Copy Bundle Resources and the gif file is there automatically when I add to the project. The weird thing is somehow the gif file is not available if it's in the Assets.xcassets.
I updated the QLImage struct from #Andrew to load the gif file by name (make sure to add the gif directly to your project as mentioned above). Here's the updated code below. You need to also provide a sample gif image called preview-gif or call it whatever you want and match the name in the preview section.
import SwiftUI
import Quartz
struct QLImage: NSViewRepresentable {
private let name: String
init(_ name: String) {
self.name = name
}
func makeNSView(context: NSViewRepresentableContext<QLImage>) -> QLPreviewView {
guard let url = Bundle.main.url(forResource: name, withExtension: "gif")
else {
let _ = print("Cannot get image \(name)")
return QLPreviewView()
}
let preview = QLPreviewView(frame: .zero, style: .normal)
preview?.autostarts = true
preview?.previewItem = url as QLPreviewItem
return preview ?? QLPreviewView()
}
func updateNSView(_ nsView: QLPreviewView, context: NSViewRepresentableContext<QLImage>) {
guard let url = Bundle.main.url(forResource: name, withExtension: "gif")
else {
let _ = print("Cannot get image \(name)")
return
}
nsView.previewItem = url as QLPreviewItem
}
typealias NSViewType = QLPreviewView
}
struct QLImage_Previews: PreviewProvider {
static var previews: some View {
QLImage("preview-gif")
}
}
This will return a view, but you need to specify the frame size or you can't see anything. Use it like this it a view:
QLImage("myGif").frame(width: 500, height: 300, alignment: .center)
Set whatever frame size is appropriate. All the other view-related properties are also available with this.
Hope this helps somebody else who is trying to display GIF images from local resources on macOS. Apple should really make it easier to display animated GIF since it's being used everywhere these days.

Why is my macOS app bypassing sandboxed file access?

I'm using macOS sandboxing and file path bookmarks for the first time in an app.
One thing that's impeding my understanding is that my app seems to be able to open any folder path, regardless of sandboxing.
For example, I can access the file structure and file contents of the launch daemons path (which I assume would be off limits to a sandboxed app).
This is my app delegate that I've modified from a clean Swift UI / AppKit App Delegate project to showcase this.
import Cocoa
import SwiftUI
#main
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let url = URL(fileURLWithPath: "/Library/LaunchDaemons")
if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey, .isDirectoryKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
for case let fileURL as URL in enumerator {
print(fileURL);
do {
let contents = try String(contentsOf: fileURL, encoding: .utf8)
print(contents)
} catch {
}
}
}
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
I have set "User Selected File" to none in the app's sandbox entitlements.
The file path and file contents are successfully printed to the console.
What am I missing here? Why is this app able to get access to this path?
FYI - I'm just running this in the Xcode debugger.

Unable to set Image of NSImageView using file path

I have hooked to Selection changed event of NSTableView.I need to display the selected image in the imageview
func tableViewSelectionDidChange(_ notification: Notification)
{
let table = notification.object as! NSTableView
print(fileArray[table.selectedRow].path);
img_view.image=NSImage(named: NSImage.Name(rawValue: fileArray[table.selectedRow].path))
}
The console prints
/Users/myname/Downloads/435_v9_bc.jpg
But the imageview does not display the image.
Update 1:
print(fileArray[table.selectedRow].path);
img_view.image=NSImage(byReferencing: URL(string: fileArray[table.selectedRow].path)!)
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Console still prints
/Users/myname/Downloads/123_1 (1).jpeg
URL(string: is the wrong API, for file system paths you have to use URL(fileURLWithPath.
img_view.image = NSImage(byReferencing: URL(fileURLWithPath: fileArray[table.selectedRow].path))
However fileArray[table.selectedRow] seems to be already an URL so there is a still easier way
img_view.image = NSImage(contentsOf: fileArray[table.selectedRow])

On-Demand Resources download but not accessible. Swif 4

In my iOS app I have a bunch of mp4 videos that I download at a certain time on the app using On Demand Resources. Using this tutorial:
https://www.raywenderlich.com/520-on-demand-resources-in-ios-tutorial
I download the resources like this at the start of the app, in a previous view controller:
func requestSceneWith(tag: String,
onSuccess: #escaping () -> Void,
onFailure: #escaping (NSError) -> Void) {
// 2
currentRequest = NSBundleResourceRequest(tags: [tag])
// 3
guard let request = currentRequest else { return }
request.beginAccessingResources { (error: Error?) in
// 4
if let error = error {
onFailure(error as NSError)
return
}
// 5
onSuccess()
}
The resource seem to download fine, and I know that they have been downloaded, by looking in the disk report in xcode.
However, when the videos are supposed to be played in the app, the app just shows a black screen. Here is my code to play the videos:
let videoURL = Bundle.main.url(forResource: "cow2", withExtension: "mp4", subdirectory: "Videos/Animals")
self.player = AVPlayer(url: videoURL!)
self.myPlayerController.player = self.player
self.myPlayerController.player?.play()
Now, when the resources are not tagged, and they come with the app and not downloaded later, they work fine. And the console prints the file name, like I did (print(videoURL.absoluteString). But after they are tagged and downloaded later, they dont work, and nothing prints in the console. Just a black screen appears in the app.
I've been stuck on this for ages, and help with really help.
Thanks
I think you didn't request the on-demand resources before accessing the video file. Maybe you can try to declare the NSBundleResourceRequest instance as a global variable in AppDelegate.

Sending an animated Gif via sms in Swift

I'm trying to send an animated gif via the MFMessageComposeViewController in swift,
It seems to be working fine when I send the gif, however the preview shows a non-moving image. Any thoughts on how to make the preview animated?
let messageComposeVC = MFMessageComposeViewController()
let imgData = NSData(contentsOfURL: NSURL(string: "http://dramallamaapp.com/wp-content/uploads/2016/08/output_9GECbQ.gif")!)
if imgData != nil {
messageComposeVC.addAttachmentData(imgData!, typeIdentifier: "com.compuserve.gif", filename: "animated.gif")
}
messageComposeVC.messageComposeDelegate = self
presentViewController(messageComposeVC, animated: true, completion: nil)
Upon further investigation, I now believe this is default iOS behavior, as with iMessages the same happens (gif shows as an image until sent, then begins animating).