Swift macOS wallpaper set fit style - swift

I managed to set the wallpaper for macOS by:
let workspace = NSWorkspace.shared
if let screen = NSScreen.main {
try workspace.setDesktopImageURL(imgurl, for: screen, options: [:])
//imgurl is the url location of the required image
}
} catch {
print(error)
}
However, how do I set the fit style (like fill screen, fit to screen, etc)? Will it be in options?

This solved it :
try workspace.setDesktopImageURL(imgurl, for: screen, options: [NSWorkspace.DesktopImageOptionKey.imageScaling : 5])
//The value of the dictionary is the desired number you want to scale the image by.

Related

Statusbar.image has wrong color

I am using following code for my status bar image:
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
This leads to wrong color for certain background colors / modes. In the picture, what's white should be black. The image resource is white/transparent. If I change that, I get the same problem. All other status bar images will turn white on certain configurations, mine will stay black.
I was thinking that MacOS would add effects to make all statusbar icons look uniform on it's own, but apparently thats not the case...
Any ideas how to fix that?
Thanks!
MacOs can do what you want. I recommend reading Apple documentation:
https://developer.apple.com/documentation/uikit/appearance_customization/supporting_dark_mode_in_your_interface
Basically you have 2 options if you don‘t provide the code manually.
Option 1. In Xcode, navigate to your image asset in assets.xcassets. In the attributes pane, in „Render as…“ specify „Template Image“. This worked well for my menu bar app.
Option 2. Supply different versions of your icon in one image asset, macOs will then choose the appropriate version.
I found a solution. Again I realize that MacOS development is way less supported by Apple than iOS. I think the color adjustment of statusbar icons should be the task of the operating system, but Apple lets the developer do the work. Whatever.
Here is the solution:
You have to provide two versions of your icon, one in black, the other in white.
When the app launches, you have to check wether the user's MacOs is in dark or light mode. This can be done with following code:
let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
if (mode == "Dark"){
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
} else {
let icon = NSImage(imageLiteralResourceName:"flagDark")
statusBarItem.image = icon
}
One problem remains here now: When the user changes the mode while your app is running, the icon color won't update. Also: If the user uses the automatic mode (i.e. it's light at day and dark in the night), the icon color won't switch as well.
You can tackle that problem by listening to a certain notification that is fired when the dark mode settings changes:
DistributedNotificationCenter.default.addObserver(self, selector: #selector(updateIcon), name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil)
#objc func updateIcon(){
print("updateIcon ausgeführt")
let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
if (mode == "Dark"){
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
} else {
let icon = NSImage(imageLiteralResourceName:"flagDark")
statusBarItem.image = icon
}
}
In my tests, this worked in all scenarios.
For me this worked:
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
// ...
func applicationDidFinishLaunching(_ aNotification: Notification) {
// ...
if let button = statusItem.button {
let image = NSImage(named: NSImage.Name("TrayIcon"))
image?.isTemplate = true
button.image = image
}
// ...
}
// ...
}

Re-downloading images with different sizes when using Kingfisher

i have an app that works with API JSON response. Just retrieving a simple array of items. Each item has an url with a high-res image. I'm using Kingfisher to get this images and resize to my ImageView size (that placed on my MainView)
//MainViewController
itemImageMainVC.kf.setImage(
with: URL(string: itemModel.artImage),
options: [
.processor(DownsamplingImageProcessor(size: itemImageMainVC.bounds.size) >> RoundCornerImageProcessor(cornerRadius: 16, targetSize: itemImageMainVC.bounds.size)),
.scaleFactor(UIScreen.main.scale),
.transition(.fade(0.2)),
.cacheOriginalImage
])
Everything is fine here, i've got my images on my MainView inside TableView, and they cached well. BUT! If i try to open and display the same images, but with different sizes on the SecondView, i see the same transition animation on the SecondView, so i guess that they started to download again when they shouldn't.
//SecondViewController
itemImageSecondVC.kf.setImage(
with: URL(string: itemModel.artImage),
options: [
.processor(DownsamplingImageProcessor(size: itemImageSecondVC.bounds.size) >> RoundCornerImageProcessor(cornerRadius: 16, targetSize: itemImageSecondVC.bounds.size)),
.scaleFactor(UIScreen.main.scale),
.transition(.fade(0.2)),
.cacheOriginalImage
])
itemImageMainVC and itemImageSecondVC have different sizes.
What i'm doing wrong? I just want to download/display images on the MainView and instantly show them when i open SecondView, without re-downloading
Lets try to use the cacheKey, it should be the last path component (or event the absoluteUrlString) from your image url or something that is unique.
let resource = ImageResource(downloadURL: url, cacheKey: url.lastPathComponent)
self.imageView.kf.setImage(with: resource, placeholder: UIImage(named: "placeholder-image"), options: nil, progressBlock: nil) { (result) in
switch result {
case .success(_):
// Success case
case .failure(_):
// Failed case
}
}

Is it possible to (programmatically) set wallpapers for each separate "space"/desktop in macOS?

I'm making a small app for myself to change the desktop background image periodically.
My program contains this block of code:
let screen = NSScreen.main()!
let newWallpaperURL = URL(/* ... */)
// ...
try! NSWorkspace.shared().setDesktopImageURL(newWallpaperURL, for: screen, options: [:])
This works, but only for the current "space" the keyboard is focused on.
e.g. if I'm in a fullscreen app, only the background of the Space occupied fullscreen app will be changed (not the background of my normal desktop).
If I have two Spaces/desktops, it only changes the background image of one of them.
Is it possible to individually set wallpapers for each space programmatically?
You can get all the screens and set all of them.
let screens = NSScreen.screens
let newWallpaperURL = URL(/* ... */)
for i in screens {
try! NSWorkspace.shared().setDesktopImageURL(newWallpaperURL, for: i, options: [:])
}
Use this in Xcode 8.x:
if let screens = NSScreen.screens() {
let newWallpaperURL = URL(/* ... */))
for screen in screens {
try? NSWorkspace.shared().setDesktopImageURL(newWallpaperURL, for: screen, options: [:])
}
}
Unlike other solutions posted here this one will work in the current Xcode 8. NSScreen.screens is a class var in Xcode 9 (currently beta) but a class func in Xcode 8 which is why you need to put .screens() instead of .screens. Also, screens returns an optional so you need to safely unwrap it before passing it to the for loop.

How to access Image Assets like an array in Swift

In my app I'm allowing the user the change some of the UI elements based on color. So, I have three versions of an image which can be used for a button and I'd like to select the image programmatically:
PSEUDO CODE
"image0", "image1", "image3"
var userChoice:integer
myButton.setImage("myImage"+userChoice , .normal)
I've seen this solution in SO:
Programmatically access image assets
What would be the Swift equivalent code?
Right now I'm using image literal:
self.But_Settings.setImage(#imageLiteral(resourceName: "settingswhite"), for: UIControlState.normal)
but of course Xcode changes this part "#imageLiteral(resourceName: "settingswhite")" to an icon which cannot be edited.
Then don't use image literal. Image literals are just that - a hard value in code that you can't change during run time. Load an image dynamically from your bundle:
if let image = UIImage(named: "myImage" + userChoice) {
self.But_Settings.setImage(image, for: .normal)
}
Do you think this would help? : But_Settings.setImage(UIImage(named: "play.png"), for: UIControlState.normal). Here you are using name of the asset
Since it's a limited number of choices it sounds like a good place for an enum.
enum ImageChoice: Int {
case zero = 0, one, two
var image: UIImage {
switch self {
case .zero:
return // Some Image, can use the icon image xcode provides now
case .one:
return //another image
case .two:
return //another image
}
}
}
Then you can easily get the correct image by initializing the enum by using an Int value.
guard let userChoice = ImageChoice(rawValue: someInt) else { return //Default Image }
let image = userChoice.image

AppleTv / TvOS - Resize TopShelf Images in TVContentItems Saved to ImageURL

I am implementing TopShelf in Apple tvOS. I have downloaded images and assigned to imageURL of TVContentItems. The downloaded images aspect ratio does not fit into the TopShelf Images properly. I have tried to change the size by appending width + height to the image link.
www.mydownloadedimages.com/{width}x{height}
But it didn't work.
Can I do resizing at client end in any other way. In the TVContentItem class I have only have NSURL object. There is no UIImage Object.
Thanks a lot.
Here is Apple's documentation on Image Sizes and shapes
// ~ 2 : 3
// ~ 1 : 1
// ~ 4 : 3
// ~ 16 : 9
// ~ 8 : 3
// ~ 87 : 28
//#property imageShape
//#abstract A TVContentItemImageShape value describing the intended aspect ratio or shape of the image.
//#discussion For Top Shelf purposes: the subset of values which are //valid in this property, for TVContentItems in the topShelfItems property //of the TVTopShelfProvider, depends on the value of the topShelfStyle //property of the TVTopShelfProvider:
TVTopShelfContentStyleInset:
valid: TVContentItemImageShapeExtraWide
TVTopShelfContentStyleSectioned:
valid: TVContentItemImageShapePoster
valid: TVContentItemImageShapeSquare
valid: TVContentItemImageShapeHDTV
When the value of this property is not valid for the Top Shelf style, the system reserves the right to scale the image in any way.
You're right saying that the TVContentItem has no UIImage type property. Since TVContentItem also accepts local file URLs in the imageURL property, a workaround can be:
grabbing the UIImage from internet
creating a new image context with the size of the top shelf image
saving it into the NSCacheDirectory
setting the local image URL as imageURL.
Here are the steps:
Let's create our TVContentItem object:
let identifier = TVContentIdentifier(identifier: "myPicture", container: wrapperID)!
let contentItem = TVContentItem(contentIdentifier: identifier )!
Set the contentItem's imageShape:
contentItem.imageShape = .HDTV
Grab the image from Internet. Actually I did this synchronously, you can also try to use other async methods to get that (NSURLConnection, AFNetworking, etc...):
let data : NSData = NSData(contentsOfURL: NSURL(string: "https://s3-ak.buzzfed.com/static/2014-07/16/9/enhanced/webdr08/edit-14118-1405517808-7.jpg")!)!
Prepare the path where your image will be saved and get your UIImage from the data object:
let filename = "picture-test.jpg"
let paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)
let filepath = paths.first! + "/" + filename
let img : UIImage = UIImage(data: data)!
Assuming you've already set the topShelfStyle property, get the size of the top shelf image with the method TVTopShelfImageSizeForShape. This will be the size of your image context:
let shapeSize : CGSize = TVTopShelfImageSizeForShape(contentItem.imageShape, self.topShelfStyle)
Create your image context of shapeSize size and draw the downloaded image into the context rect. Here you can do all your modifications to the image to adjust it into the desired size. In this example I took a square image from Instagram and I put white letterbox bands on the right and left sides.
UIGraphicsBeginImageContext(shapeSize)
let imageShapeInRect : CGRect = CGRectMake((shapeSize.width-shapeSize.height)/2,0,shapeSize.height,shapeSize.height)
img.drawInRect(imageShapeInRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
In the end, save this image into your NSCacheDirectory and set the image path as contentItem's imageURL.
UIImageJPEGRepresentation(newImage, 0.8)!.writeToFile(filepath, atomically: true)
contentItem.imageURL = NSURL(fileURLWithPath: filepath)
Complete your TVContentItem with other details (like title, internal link, etc...), run the top shelf extension... et voilà!
I would like to add to Nicola Giancecchi's answer. The Top Shelf extension will run on a non main thread so using methods like UIImageJPEGRepresentation or UIImagePNGRepresentation will sometimes stop the Top Shelf thread printing this in the console:
Program ended with exit code: 0
To fix this you can wrap your code like this:
DispatchQueue.main.sync {
UIImageJPEGRepresentation(newImage, 0.8)!.writeToFile(filepath, atomically: true)
let imageURL = NSURL(fileURLWithPath: filepath)
if #available(tvOSApplicationExtension 11.0, *) {
contentItem.setImageURL(imageURL, forTraits: .userInterfaceStyleLight)
contentItem.setImageURL(imageURL, forTraits: .userInterfaceStyleDark)
} else {
contentItem.imageURL = imageURL
}
}