Cannot find "UIScreen" in scope [duplicate] - swift

This question already has an answer here:
Adjust screen brightness in Mac OS X app
(1 answer)
Closed last year.
I've just approached Swift, for building a quick utility app for my MacBook.
I have written the following code, but it gives me the error "Cannot find 'UIScreen' in scope".
class AppDelegate: NSObject,NSApplicationDelegate{
//...
func applicationDidFinishLaunching(_ notification: Notification) {
Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
UIScreen.mainScreen().brightness = CGFloat(0.0) //<- error here
}
}
//...
}
Initially I thought I've missed an import, but I cannot find what to import (UIKit, is only for iOS).
Thank you for any help

Anything beginning with "UI" is an iOS class. Those classes are not available from a macOS app.
The equivalent class for macOS is probably NSScreen, although I don't think it has an exposed brightness property like the one you are trying to set.
Edit:
It looks like it is possible to set the screen brightness in MacOS, but you have to use much lower-level code. Check out this link.
The relevant code from that link is the following, which uses IOKit:
func setBrightnessLevel(level: Float) {
var iterator: io_iterator_t = 0
if IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"), &iterator) == kIOReturnSuccess {
var service: io_object_t = 1
while service != 0 {
service = IOIteratorNext(iterator)
IODisplaySetFloatParameter(service, 0, kIODisplayBrightnessKey, level)
IOObjectRelease(service)
}
}
}

Related

A Swift input method switcher works only after changing focus to another window

I am trying to write a MacOS app which switch input methods by previously assigned shortcut(command+space in here).
Switching input methods preoperly works so that the language icon at the status bar(top right) immediately changes as I put the shortcut.
The problem I got in here is that the actual input method does not change.
For example, if I run my app when the selected input method is Korean, then although the status bar is showing the selected input method is Japanese after command+space, what I can only type is Korean characters.
However, after I change focus to another text app(e.g. from sublime text to xcode), only then the selected input method is reflected well.
I am using MacOS Monterey 12.6 and Xcode 13.1.
My project contains two source files.
The code in the file AppDelegate.swift is as follows:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var switcher = Switcher()
}
And the code in the file Switcher.swift is as follows:
import Cocoa
import MASShortcut
class Switcher{
var lang: Int = 0
var kr: TISInputSource?
var jp: TISInputSource?
var en: TISInputSource?
init(){
let inputSourceNSArray = TISCreateInputSourceList(nil, false).takeRetainedValue() as NSArray
let inputSourceList = inputSourceNSArray as! [TISInputSource]
for inputSource in inputSourceList {
if inputSource.id == "com.apple.inputmethod.Korean.2SetKorean" {
self.kr = inputSource
}
if inputSource.id == "com.apple.inputmethod.Kotoeri.RomajiTyping.Japanese" {
self.jp = inputSource
}
if inputSource.id == "com.apple.keylayout.ABC" {
self.en = inputSource
}
}
self.register()
}
func switchLang(){
self.lang = (self.lang + 1) % 3
switch lang {
case 0:
TISSelectInputSource(self.kr)
case 1:
TISSelectInputSource(self.jp)
case 2:
TISSelectInputSource(self.en)
default:
print("error")
}
}
func register() {
let langShortcut = MASShortcut(keyCode: kVK_Space, modifierFlags: [.command])
MASShortcutMonitor.shared()?.register(langShortcut, withAction: {
self.switchLang()
})
}
}
I wrote these codes by referring KAWA, but KAWA does not make this issue.
I have analyzed all codes of KAWA several times, I couldn't find out why the same problem does not occur in KAWA.
I am quite new to Swift, and I have no idea to approach.
Could you help me....? Thank you.

Xcode warning: 'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead

when updating my App's Deployment target to 15.0, i receive this warning:
'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a
relevant window scene instead
I have tried to look on the net what could be done to remediate this, but couldn't find much info on this. Hope you could share some advice.
The line of code i am using where this alert occurred is:
let window = UIApplication.shared.windows[0]
followed by in my ViewDidLoad():
DispatchQueue.main.async { [weak self] in
if defaults.bool(forKey: "darkModeBoolSwitch") == true {
self?.window.overrideUserInterfaceStyle = .dark
} else if defaults.bool(forKey: "darkModeBoolSwitch") == false {
self?.window.overrideUserInterfaceStyle = .light
}
An alternative to #DuncanC's solution that may also work for you: UIApplication has a connectedScenes property, which lists all of the currently-active scenes doing work in your application (for most applications, this is just the one main scene).
Of those scenes, you can filter for scenes which are UIWindowScenes (ignoring scenes which are not currently active and in the foreground), and of those, find the first scene which has a window which is key:
extension UIApplication {
static var firstKeyWindowForConnectedScenes: UIWindow? {
UIApplication.shared
// Of all connected scenes...
.connectedScenes.lazy
// ... grab all foreground active window scenes ...
.compactMap { $0.activationState == .foregroundActive ? ($0 as? UIWindowScene) : nil }
// ... finding the first one which has a key window ...
.first(where: { $0.keyWindow != nil })?
// ... and return that window.
.keyWindow
}
}
I hesitate to call this extension something like UIApplication.keyWindow because the reason for the deprecation of these APIs is because of the generalization to multi-scene applications, each of which may have its own key window... But this should work.
If you still need to support iOS 14, which does not have UIWindowScene.keyWindow, you can replace the firstWhere(...)?.keyWindow with: flatMap(\.windows).first(where: \.isKeyWindow).
I am out-of-date with Apple's recent changes to implement scenes.
I did a little digging, and found a protocol UIWindowSceneDelegate
It looks like you are supposed to add an "Application Scene Manifest" to your app's info.plist file that tells the system the class that serves as the app's window scene delegate.
Then in that class you want to implement the method scene(_:willConnectTo:options:). When that method is called you sould try to cast the UIScene that's passed to you to to a UIWindowScene, and if that cast succeeds, you can ask the window scene for it's window and save it to an instance property.
That should allow you to save a pointer to your app's window and use it when needed.

SwiftUI and macOS Option-click a Button

Hi how can I detect when a user option-clicks a Button?
Button("Example") {
if optionKeyDown {
// A
} else {
// B
}
}
I am using the SwiftUI App Lifecycle.
I don't know of a pure SwiftUI way of doing it, but SwiftUI for macOS apps still needs work to really get the UI experience Mac users expect.
However, you can use CoreGraphics.CGEventSource to achieve what you want:
import CoreGraphics
extension CGKeyCode
{
static let kVK_Option : CGKeyCode = 0x3A
static let kVK_RightOption: CGKeyCode = 0x3D
var isPressed: Bool {
CGEventSource.keyState(.combinedSessionState, key: self)
}
static var optionKeyPressed: Bool {
return Self.kVK_Option.isPressed || Self.kVK_RightOption.isPressed
}
}
The specific key code names in the extension aren't particularly Swifty, so rename them if you like. Those particular names go back to the Classic MacOS Toolbox, and were used in the early days of OS X in the Carbon Event Manager. I tend to keep them for historical reasons. I've created a github gist with of all the old key codes.
With that extension in place, using it is straight-forward. Your code snippet becomes
Button("Example") {
if CGKeyCode.optionKeyPressed {
// A
} else {
// B
}
}

Is it possible to use Xcode UI Testing on an app using SpriteKit?

I want to use UI testing for a game using SKSpriteKit.
As my first tries did not work I wonder if it possible to use Xcode UI Testing with SpriteKit.
The main idea is to create the accessibility material for elements that you want to UI test. That's mean:
List all accessible elements contained in the scene
Configure settings for each of these elements, especially framedata.
Step by Step
This answer is for Swift 3 and is mainly based on Accessibility (Voice Over) with Sprite Kit
Let's say I want to make the SpriteKit button named tapMe accessible.
List of accessible elements.
Add an array of UIAccessibilityElementto the Scene.
var accessibleElements: [UIAccessibilityElement] = []
Scene's cycle life
I need to update two methods: didMove(to:)and willMove(from:).
override func didMove(to view: SKView) {
isAccessibilityElement = false
tapMe.isAccessibilityElement = true
}
As scene is the accessibility controller, documentation stated it must return False to isAccessibilityElement.
And:
override func willMove(from view: SKView) {
accessibleElements.removeAll()
}
Override UIAccessibilityContainer methods
3 methods are involved: accessibilityElementCount(), accessibilityElement(at index:) and index(ofAccessibilityElement. Please allow me to introduce an initAccessibility() method I'll describe later.
override func accessibilityElementCount() -> Int {
initAccessibility()
return accessibleElements.count
}
override func accessibilityElement(at index: Int) -> Any? {
initAccessibility()
if (index < accessibleElements.count) {
return accessibleElements[index]
} else {
return nil
}
}
override func index(ofAccessibilityElement element: Any) -> Int {
initAccessibility()
return accessibleElements.index(of: element as! UIAccessibilityElement)!
}
Initialize accessibility for the Scene
func initAccessibility() {
if accessibleElements.count == 0 {
// 1.
let elementForTapMe = UIAccessibilityElement(accessibilityContainer: self.view!)
// 2.
var frameForTapMe = tapMe.frame
// From Scene to View
frameForTapMe.origin = (view?.convert(frameForTapMe.origin, from: self))!
// Don't forget origins are different for SpriteKit and UIKit:
// - SpriteKit is bottom/left
// - UIKit is top/left
// y
// ┌────┐ ▲
// │ │ │ x
// ◉────┘ └──▶
//
// x
// ◉────┐ ┌──▶
// │ │ │
// └────┘ y ▼
//
// Thus before the following conversion, origin value indicate the bottom/left edge of the frame.
// We then need to move it to top/left by retrieving the height of the frame.
//
frameForTapMe.origin.y = frameForTapMe.origin.y - frameForTapMe.size.height
// 3.
elementForTapMe.accessibilityLabel = "tap Me"
elementForTapMe.accessibilityFrame = frameForTapMe
elementForTapMe.accessibilityTraits = UIAccessibilityTraitButton
// 4.
accessibleElements.append(elementForTapMe)
}
}
Create UIAccessibilityElement for tapMe
Compute frame data on device's coordinates. Don't forget that frame's origin is the top/left corner for UIKit
Set data for UIAccessibilityElement
Add this UIAccessibilityElement to list of all accessible elements in scene.
Now tapMe is accessible from UI testing perspective.
References
Session 406, UI Testing in Xcode, WWDC 2015
eyes off eyes on — Voiceover accessibility in SpriteKit
How do I support VoiceOver in a SpriteKit game? | Apple Developer Forums
swift - Accessibility (Voice Over) with Sprite Kit - Stack Overflow
According to Apple developer forum discussion, Integrate UITest with SpriteKit is not currently possible:
This is likely not currently possible, but it probably could be
Update 2017-02-19
According to comment by #ChrisLivdahl this may be achieved by using UIAccessibility — Session 406, UI Testing in Xcode, WWDC 2015.
The idea is to make the element needed UI Testable.
Update early 2021
A much shorter solution tested with SpriteKit, SwiftUI App and Swift 5.4
The earlier approaches didn't seem to work anymore when using XCUI Tests. The basis of my app is a SwiftUI app that has a SKScene as its main view. In order for it to work it was at the end quite simple actually and required much less steps for me to work.
1. Deactivate accessibility of the scene by adding only one line to the didMove() method
override func didMove(to view: SKView) {
isAccessibilityElement = false
}
As mentioned in Dominique Vial's answer
2. Conforming your nodes to UIAccessibilityIdentification protocol
class YourNode: SKSpriteNode, UIAccessibilityIdentification {
var accessibilityIdentifier: String?
//...
}
Mentioned here
Any node that needs to be accessible by the UI Test needs to conform to this protocol. Update Extension, instead of subclassing every node I'm using now the extension below.
3. Assign the accessibilityIdentifier and activate accessibility of your nodes objects.
let yourNode = YourNode()
yourNode.isAccessibilityElement = true
yourNode.accessibilityIdentifier = "nodeID"
4. That's it! Run your tests!
func testingNodes() throws {
app = XCUIApplication()
app.launch()
let node = app.otherElements["nodeID"]
XCTAssert(node.waitForExistence(timeout: 1))
}
5. Optional: set accessibilityTraits
yourNode.accessibilityTraits = [.button, .updatesFrequently]
let nodeAsButton = app.buttons["nodeID"]
You can set specific trait like .button to tell the accessibility API what your node is. This is useful to differentiate your nodes better during testing but also if you're planning to implement actual accessibility features for the user than this should be set correctly for it to work.
Update 1 - Using Extensions:
Instead of subclassing the nodes I'm using now the following extension on SKNode. I now only set the accessibilityLabel an no longer the accessibilityIdentifier
extension SKNode: UIAccessibilityIdentification {
public var accessibilityIdentifier: String? {
get {
super.accessibilityLabel
}
set(accessibilityIdentifier) {
super.accessibilityLabel = accessibilityIdentifier
}
}
}
Update 2 - Making all descendants of SKScene accessible
To allow for nodes and all of their descendants to be accessible I've ended up using the following extension on SKNode. This adds each node to the scene's accessibilityElements.
extension SKNode: UIAccessibilityIdentification {
public var accessibilityIdentifier: String? {
get {
super.accessibilityLabel
}
set(accessibilityIdentifier) {
super.accessibilityLabel = accessibilityIdentifier
}
}
func makeUITestAccessible(label: String, traits: UIAccessibilityTraits) {
accessibilityLabel = label
isAccessibilityElement = true
accessibilityTraits = traits
if let scene = scene {
if scene.accessibilityElements == nil {
scene.accessibilityElements = [self]
} else {
scene.accessibilityElements?.append(self)
}
}
}
}
I've implemented an example project based on your (#Domsware's) awesome answer, and I've confirmed this trick works well for both Xcode UI Testing Framework and KIF.
Hope this example helps for anyone who is interested in this topic :)

Swift playground in Xcode 6.2: println doesn't print anything inside willSet/didSet

I've just started learning Swift after a long way with Objective-C. As recommended by Apple, I've chosen playgrounds to code and learn. Here's the code:
class Polygon
{
var color:UIColor = UIColor.clearColor()
var numberOfSides:Int = 0 {
willSet(newNumber)
{
println("willSet is being called")
}
didSet(newNumber)
{
println("didSet is being called")
//just to make sure didSet is called
numberOfSides = 12345678
}
}
}
var polygon1 = Polygon()
polygon1.numberOfSides = 222222
polygon1.numberOfSides = 1
var polygon2 = Polygon()
polygon2.numberOfSides = 12313
It's as simple as 2+2, and the polygons behave as expected. The question is: where is the result of println() calls (see the image embedded)? Thanks in advance.
Output of print/println functions goes to console - you can see it in assistant editor (use app menu View -> Assistant editor -> Show assistant editor to show it, or keyboard alt+cmd+enter shortcut)