Swift - cycling through background images - image

I'm completely new to coding and I'm trying to learn Swift. I'm trying to cycle through background images for an app. The images I have are named 1.jpg, 2.jpg, 3.jpg, 4.jpg. When I try run the simulator, the "super.viewDidLoad()" line is highlighted green with the comment, "Thread 1, Breakpoint 3.1." Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var imageList = [UIImage]()
for i in 1...4 {
let imageName = "\(i).jpg"
var image = UIImage(named:imageName)
imageList.append(image)
}
self.myImageView.animationImages = imageList
self.myImageView.animationDuration = 4.0
self.myImageView.startAnimating()

It sounds like you've set a breakpoint there. If there's a blue pointer to the left of the line, click it to disable it. Or, use the keyboard shortcut command-Y to disable all breakpoints.

Related

NSAppearance is not updating when toggling dark mode

I have a macOS app that runs only in the macOS status bar. I changed the "Application is agent (UIElement)" property in the Info.plist to "YES":
<key>LSUIElement</key>
<true/>
I have a timer that prints out the appearance's name every 5 seconds like this:
Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in
let appearance = NSAppearance.currentDrawing()
print(appearance.name)
}
Problem
The name doesn't actually change when I toggle dark/light mode in system settings. It always prints the name of the appearance that was set when the application launched.
Is there a way to listen to system appearance changes?
Goal
My end goal is actually to draw an NSAttributedString to an NSImage, and use that NSImage as the NSStatusItem button's image.
let image: NSImage = // generate image
statusItem.button?.image = image
For the text in the attributed string I use UIColor.labelColor that is supposed to be based on the system appearance. However it seems to not respect the system appearance change.
When I start the application in Dark Mode and then switch to Light Mode:
When I start the application in Light Mode and then switch to Dark Mode:
Side note
The reason why I turn the NSAttributedString into an NSImage and don't use the NSAttributedString directly on the NSStatusItem button's attributedTitle is because it doesn't position correctly in the status bar.
The problem with drawing a NSAttributedString is, that NSAttributedString doesn't know how to render dynamic colors such as NSColor.labelColor. Thus, it doesn't react on appearance changes. You have to use a UI element.
Solution
I solved this problem by passing the NSAttributedString to a NSTextField and draw that into an NSImage. Works perfectly fine.
func updateStatusItemImage() {
// Use UI element: `NSTextField`
let attributedString: NSAttributedString = ...
let textField = NSTextField(labelWithAttributedString: attributedString)
textField.sizeToFit()
// Draw the `NSTextField` into an `NSImage`
let size = textField.frame.size
let image = NSImage(size: size)
image.lockFocus()
textField.draw(textField.bounds)
image.unlockFocus()
// Assign the drawn image to the button of the `NSStatusItem`
statusItem.button?.image = image
}
React on NSAppearance changes
In addition, since NSImage doesn't know about NSAppearance either I need to trigger a redraw on appearance changes by observing the effectiveAppearance property of the button of the NSStatusItem:
observation = statusItem.observe(\.button?.effectiveAppearance, options: []) { [weak self] _, _ in
// Redraw
self?.updateStatusItemImage()
}

xcode 7.3 reference icon from assets folder for prototype table view cell

In Xcode 7 with swift, I'm trying to use icons from my assets folder to display in the icon slot in a normal prototype tableview cell.
My code compiles and the app runs fine, displaying all the proper row text titles in the table, but all the cells use an icon that I (perhaps stupidly) entered into the attributes inspector for the prototype table cell.
By default, the style=Basic table cell does not have an icon to the left of the Title in the storyboard. BUT... if you add the name of an icon image from the assets folder in the image=MyAssetsIconName, the UI builder automatically adds a UIImageView to the left end of the table row cell. And of course all rows display the image that you selected. Which is what my app currently does.
But I would like to change the icon image for each row, so that the image on any row matches the text rows that I display. So I tried to assign a new image (from the assets folder) to each row cell as it was created. I did the assignment right after I assigned the text title to the cell.
Here is my code, which runs fine (but doesn't display the images that I want).
class TsbReportsCell: UITableViewCell {
#IBOutlet weak var Title: UILabel!
#IBOutlet weak var Detail: UILabel!
#IBOutlet weak var Icon: UIImageView!
...
}
In the table view controller:
In the table view controller class:
// define datasources
var reportnames = [String]()
var reportimages = [String]()
override func viewDidLoad() {
super.viewDidLoad()
reportnames = ["Balance Report",
"Routine Report",
"Low Quality Report"]
reportimages = ["enter.png",
"exterior.png",
"export.png"]
}
override func tableView(tableView:.... {
// all this code works fine
let cell = tableView.dequeueReusableCellWithIdentifier...
let row = indexPath.row
cell.Title.text = reportnames[row]
// here are the lines that seem to have no effect at all
let rowimage = UIImage(named: reportimages[row])
cell.Icon = UIImageView(image: rowimage)
return cell
}
Any ideas what I'm doing wrong? I named an image in the attributes for the table cell row just to get the UI builder to add the imageview placeholder for me. But maybe I'm not allowed to ever override that image, even though I can create an outlet for it.
This seems so simple a problem, but I searched at least 20 posts, the net, the doc, and I still couldn't find anything. Thanks
It's very simple. You are saying:
cell.Icon = UIImageView(image: rowimage)
But that does not put this image view into the interface! It merely assigns it to the cell's property.
Instead, you need to have an image view in the cell and hook an outlet from the cell's Icon property to that image view. As a result, cell.Icon is the image view in the interface. Now you say
cell.Icon.image = rowimage
Man, are you ever fast at answering questions, Matt. I'm sure I'm not the only one who appreciates that.
It's funny how taking a half hour to write up a decent question for this site makes you start thinking about other things to try. I took a seg fault on one of my tries, and was poking around in the error messages. From them, I got the idea that an ImageView was a structure that had an "image" field in it (or something like that), so I tried some UIImageView x UIImage crosses to see what would happen.
Sure enough, I was trying to assign an ImageView to an Image (I think; I could still be wrong). Here's the code line that worked for me.
cell.Icon.image = UIImage(named: reportimages[row])
So you can see I'm assigning an image to an image here, which works properly. AND... it must be right, because it's the same code as Matt's!! :-)

How can I create a label text with shadow?

I'm trying to create a label text with a shadow created automatically by the system. In iOS I know that you can use an Attributed style for the label, but in OS X I can't find this option. Could you help?
You can do that with Core Animation (a.k.a. CALayer). Views in iOS are layer-backed by default, but NSView on Mac OS X are much older and requires you to turn on layer manually.
The easiest way is to go to the View Effects Inspector (Cmd + Alt + 8):
If you want to add the shadow programmatically:
override func awakeFromNib() {
let shadow = NSShadow()
shadow.shadowOffset = NSMakeSize(5,5)
shadow.shadowBlurRadius = 5
shadow.shadowColor = NSColor.blackColor()
myLabel.superview?.wantsLayer = true
myLabel.shadow = shadow
}

NSButton won't let me set imagePosition property

I'm trying to build a menubar application in swift. I want to set an image and text for the status item. Evidently NSStatusItem has deprecated setting a custom view since 10.10, which is fine, since I am able to set an image and text on the status item's button. However, I'm unable to set the imagePosition property for some reason and so the text and the image overlap.
This is my code:
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
func applicationDidFinishLaunching(aNotification: NSNotification) {
let icon = NSImage(named: "statusIcon")
icon!.setTemplate(true) // best for dark mode
statusItem.button!.image = icon
statusItem.button!.imagePosition = ImageLeft
statusItem.button!.title = "Hello, world"
statusItem.menu = menu;
}
The problem is that Xcode gives me an error on this line:
statusItem.button!.imagePosition = ImageLeft
It says "Use of unresolved identifier 'ImageLeft'", but from what I can tell from the documentation (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSCell_Class/index.html#//apple_ref/c/tdef/NSCellImagePosition) that is the identifier I would want to use.
Can anyone help?
I figured it out. Evidently I have much to learn about Swift syntax.
This line allows it to work:
statusItem.button!.imagePosition = NSCellImagePosition.ImageLeft

Cocoa: take screenshot of desktop wallpaper (without icons and windows)

Is is possible to capture the Mac OS X desktop without desktop items and any windows that may be open (i.e. just the wallpaper)?
I've experimented with CGWindowListCreateImage, CGWindowListCreateImageFromArray, and CGDisplayCreateImage, but no luck.
Essentially I'm trying to capture the desktop wallpaper without using [NSWorkspace desktopImageURLForScreen:] (it's a sandboxed app without access to the file system).
You'll need to be careful to test that this is still correct, but the desktop window sits below the Finder (it's drawn by the Dock). Passing the kCGWindowListOptionOnScreenBelowWindow CGWindowListOption to CGWindowListCreateImage should get you what you want (unless something else is drawing below that level).
Otherwise, you'll need to use CGWindowListCreate and iterate through the response excluding anything that isn't drawn by the dock at the window level kCGMinimumWindowLevel + 19.
It gets kind of tricky when there are multiple screens, but hopefully this information is enough for you to do what you need.
I know this is a super old question, and Tony Arnold's question is right, and what I used to build my own "grab the desktop" code.
I have some example code that shows how to do all these things (it's a wonderful thing walking in parts of Cocoa that are barely documented... )
I've put that sample code up in a bitbucket repository. Specifically the code sample to take a picture. (There's more interesting Cocoa code in my learning Cocoa repository, where that sample code is from )
Swift version:
extension NSImage {
static func desktopPicture() -> NSImage {
let windows = CGWindowListCopyWindowInfo(
CGWindowListOption.OptionOnScreenOnly,
CGWindowID(0))! as NSArray
var index = 0
for var i = 0; i < windows.count; i++ {
let window = windows[i]
// we need windows owned by Dock
let owner = window["kCGWindowOwnerName"] as! String
if owner != "Dock" {
continue
}
// we need windows named like "Desktop Picture %"
let name = window["kCGWindowName"] as! String
if !name.hasPrefix("Desktop Picture") {
continue
}
// wee need the one which belongs to the current screen
let bounds = window["kCGWindowBounds"] as! NSDictionary
let x = bounds["X"] as! CGFloat
if x == NSScreen.mainScreen()!.frame.origin.x {
index = window["kCGWindowNumber"] as! Int
break
}
}
let cgImage = CGWindowListCreateImage(
CGRectZero,
CGWindowListOption(arrayLiteral: CGWindowListOption.OptionIncludingWindow),
CGWindowID(index),
CGWindowImageOption.Default)!
let image = NSImage(CGImage: cgImage, size: NSScreen.mainScreen()!.frame.size)
return image
}
}

Resources