Swift 3: how to write this for(;;) loop - for-loop

Looks like Apple doesn't like C loops, but doesn't provide good approach over it (or I couldn't find it). I have such loop to go from some view to the root in UI hierarchy:
for var parentView = view; parentView != nil; parentView = parentView.parent {
...
}
How to write this in Swift 3 manner?

This would be a way to do it in Swift 3:
var parentView: View! = view
while parentView != nil {
// Do stuff
parentView = parentView.parent
}
If you want to group the loop progression stuff next to while and not at the end of block, you may use defer, like this:
var parentView: View! = view
while parentView != nil {
defer { parentView = parentView.parent }
// Do stuff
}
If you want to limit the scope of parentView, you can encapsulate everything in a do block:
do {
var parentView: View! = view
while parentView != nil {
defer { parentView = parentView.parent }
// Do stuff
}
}
But it's quite verbose so you could define a new generic function for similar loops, like this:
func kindaCStyleLoop<T>(first: T, obtainNext: T -> T?, action: T -> ()) {
var current: T! = first
repeat {
action(current)
current = obtainNext(current)
} while current != nil
}
kindaCStyleLoop(view, obtainNext: { $0.parent }) {
// Do stuff with $0
}
And a last one that relies on GeneratorType and SequenceType to enable using the for-in-loop syntax:
struct CStyleGenerator<T> : GeneratorType, SequenceType {
let getNext: T -> T?
var current: T!
init(first: T, getNext: T -> T?) {
self.getNext = getNext
self.current = first
}
mutating func next() -> T? {
defer {
if current != nil {
current = getNext(current)
}
}
return current
}
}
for parentView in CStyleGenerator(first: view, getNext: { $0.parent }) {
// Do stuff with parentView
}

Correct but too-late answer: there are built-in functions in Swift 3 which provide the solution:
public func sequence<T>(first: T, next: (T) -> T?) -> UnfoldSequence<T, (T?, Bool)>
public func sequence<T, State>(state: State, next: (inout State) -> T?) -> UnfoldSequence<T, State>
We can use them in this manner:
sequence(first: view, next: {
// do something with $0...
return $0.superview
})

For instance
for view in views where view.superview != nil {
}

Related

Where to call NotificationCenter.default.addObserver() ? in my Xcode Game Project

Where to call NotificationCenter.default.addObserver() ? in my Xcode Game Project
I successfully call the following from my func application (AppDelegate), but when I toggle the Gamepad on/off, my selectors are not being called.
class GameScene: SKScene {
func ObserveForGameControllers() {
// print("ObserveForGameControllers")
NotificationCenter.default.addObserver(
self,
selector: #selector(connectControllers),
name: NSNotification.Name.GCControllerDidConnect,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(disconnectControllers),
name: NSNotification.Name.GCControllerDidDisconnect,
object: nil)
} // ObserveForGameControllers
}
My selectors look like this:
#objc func connectControllers() {}
#objc func disconnectControllers() {}
One last thing:
Here are my Gamepad settings in my Project
It seems I really need some suggestions here.
Appreciate it.
EDIT
I have been in contact with a very talented jrturton on trying to discover why I am unable to detect the presence of my Gamepad as documented above.
He has asked for a more complete presentation of my Swift code. I initially thought of Dropbox, but he has asked for this EDIT .. so here goes:
I began with a iOS Game Project which presented me with AppDelegate, GameScene, GameViewController + Storyboard.
I’ve already covered AppDelegate above, which per jrturton’s recommendation is now reduced to the standard AppDelegate func’s which essentially are empty, such as:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// currently empty
}
Next, the GameScene ..
import SwiftUI
import WebKit
import SpriteKit
import GameplayKit
import GameController
class GameScene: SKScene {
override func sceneDidLoad() {
super.sceneDidLoad()
// print("sceneDidLoad")
ObserveForGameControllers()
} // sceneDidLoad
func ObserveForGameControllers() {
// print("ObserveForGameControllers")
NotificationCenter.default.addObserver(
self,
selector: #selector(connectControllers),
name: NSNotification.Name.GCControllerDidConnect,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(disconnectControllers),
name: NSNotification.Name.GCControllerDidDisconnect,
object: nil)
} // ObserveForGameControllers
#objc func connectControllers() {
// print("CONNECT")
self.isPaused = false
var indexNumber = 0
for controller in GCController.controllers() {
if controller.extendedGamepad != nil {
controller.playerIndex = GCControllerPlayerIndex.init(rawValue: indexNumber)!
indexNumber += 1
setupControllerControls(controller: controller)
}
}
} // connectControllers
#objc func disconnectControllers() {
// print("DIS-CONNECT")
self.isPaused = true
} // disconnectControllers
func setupControllerControls(controller: GCController) {
controller.extendedGamepad?.valueChangedHandler = {
(gamepad: GCExtendedGamepad, element: GCControllerElement) in
self.controllerInputDetected(gamepad: gamepad,
element: element,
index: controller.playerIndex.rawValue)
}
} // setupControllerControls
func controllerInputDetected(gamepad: GCExtendedGamepad,
element: GCControllerElement,
index: Int) {
// A-Button
if (gamepad.buttonA == element)
{
if (gamepad.buttonA.value != 0)
{
// These print(..) statements will be replaced later
// by code to access my Javascript methods.
print("movePaddleDown")
}
}
// B-Button
else if (gamepad.buttonB == element)
{
if (gamepad.buttonB.value != 0)
{
print("movePaddleRight")
}
}
// Y-Button
else if (gamepad.buttonY == element)
{
if (gamepad.buttonY.value != 0)
{
print("movePaddleUp")
}
}
// X-Button
else if (gamepad.buttonX == element)
{
if (gamepad.buttonX.value != 0)
{
print("movePaddleLeft")
}
}
// leftShoulder
else if (gamepad.leftShoulder == element)
{
if (gamepad.leftShoulder.value != 0)
{
print("cyclePages")
}
}
// rightShoulder
else if (gamepad.rightShoulder == element)
{
if (gamepad.rightShoulder.value != 0)
{
print("newGame")
}
}
// leftTrigger
else if (gamepad.leftTrigger == element)
{
if (gamepad.leftTrigger.value != 0)
{
print("pauseGame")
}
}
// rightTrigger
else if (gamepad.rightTrigger == element)
{
if (gamepad.rightTrigger.value != 0)
{
print("resumeGame")
}
}
// Left Thumbstick
else if (gamepad.leftThumbstick == element)
{
if (gamepad.leftThumbstick.xAxis.value > 0)
{
print("movePaddleRight")
}
else if (gamepad.leftThumbstick.xAxis.value < 0)
{
print("movePaddleLeft")
}
else if (gamepad.leftThumbstick.xAxis.value == 0)
{
print("decreaseSpeed")
}
else if (gamepad.leftThumbstick.yAxis.value > 0)
{
print("movePaddleDown")
}
else if (gamepad.leftThumbstick.yAxis.value < 0)
{
print("movePaddleUp")
}
else if (gamepad.leftThumbstick.yAxis.value == 0)
{
print("decreaseSpeed")
}
}
// Right Thumbstick
if (gamepad.rightThumbstick == element)
{
if (gamepad.rightThumbstick.xAxis.value > 0)
{
print("movePaddleRight")
}
else if (gamepad.rightThumbstick.xAxis.value < 0)
{
print("movePaddleLeft")
}
else if (gamepad.rightThumbstick.xAxis.value == 0)
{
print("decreaseSpeed")
}
else if (gamepad.rightThumbstick.yAxis.value > 0)
{
print("movePaddleDown")
}
else if (gamepad.rightThumbstick.yAxis.value < 0)
{
print("movePaddleUp")
}
else if (gamepad.rightThumbstick.yAxis.value == 0)
{
print("decreaseSpeed")
}
}
// D-Pad
else if (gamepad.dpad == element)
{
if (gamepad.dpad.xAxis.value > 0)
{
print("scrollWindowRight")
}
else if (gamepad.dpad.xAxis.value < 0)
{
print("scrollWindowLeft")
}
else if (gamepad.dpad.yAxis.value > 0)
{
print("scrollWindowDown")
}
else if (gamepad.dpad.yAxis.value < 0)
{
print("scrollWindowUp")
}
}
} // controllerInputDetected
} // class GameScene: SKScene
Now, the GameViewController ..
import UIKit
import SpriteKit
import GameplayKit
import WebKit
// This is now available across Classes
var theWebView: WKWebView!
class GameViewController: UIViewController, WKNavigationDelegate {
override func loadView() {
// print("loadView")
let webConfiguration = WKWebViewConfiguration()
theWebView = WKWebView(frame: .zero, configuration: webConfiguration)
theWebView.navigationDelegate = self
view = theWebView
} // loadView
override func viewDidLoad() {
super.viewDidLoad()
// print("viewDidLoad")
loadURL(webAddress: "https://www.lovesongforever.com/firstgame")
} // viewDidLoad
func loadURL(webAddress: String) {
let theURL = URL(string: webAddress)
let theRequest = URLRequest(url: theURL!)
theWebView.load(theRequest)
theWebView.allowsBackForwardNavigationGestures = false
} // loadURL
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
}
else {
return .all
}
} // supportedInterfaceOrientations
override var prefersStatusBarHidden: Bool {
return true
} // prefersStatusBarHidden
} // class GameViewController
Note that when I RUN my iOS App, thanks to the overridden loadView() above, it presents the following in the Simulator:
Simulator presentation
But, that’s is as far as it goes, because pressing all the buttons on my Gamepad does not result in detection of my Gamepad, as evidenced when I UN-comment all the above print(..) statements. In particular, those within:
#objc func connectControllers() and
#objc func disconnectControllers() and
func controllerInputDetected( .. )
So, hopefully that is all there currently is ..
You are doing this in your app delegate:
let itsGameScene = GameScene()
itsGameScene.ObserveForGameControllers()
You're creating an instance of GameScene, making it listen for notifications (adding self as the observer)... and then probably throwing that instance away.
Your game isn't actually using SpriteKit, and SpriteKit isn't needed to deal with game controllers, you just need GameController.
You have no need to create this scene at all. The best place to observe the game controller notifications would be in your GameViewController in viewDidLoad. Move the code (the observation method and the controller-related ones) you had in your scene into the view controller, and delete the scene file.

SwiftUI - Dynamic NSPredicate If Statement

How can I create a predicate so that when the user selects "Full Body" it returns the entire list with no predicate? Right now, it is returning "part" which corresponds to the muscle groups I have set (Abs, Legs, Push, Pull). I want to return all of the options when "Full Body" is selected. How could I write an If statement so that the predicate is not used?
import SwiftUI
var parts = ["Abs", "Legs", "Push", "Pull", "Full Body"]
struct ExerciseList: View {
#State private var selectedPart = " "
var body: some View {
NavigationView {
VStack (alignment: .leading) {
NavigationLink(destination: AddExerciseView()){
Text("Add Exercise")
.fontWeight(.bold)
}
Picker("Body Part", selection: $selectedPart) {
ForEach(parts, id:\.self) { part in
Text(part)
}
}.pickerStyle(.segmented)
ListView(part:selectedPart)
}
}
}
}
import SwiftUI
struct ListView: View {
var part: String
#FetchRequest var exercises: FetchedResults<Exercise>
init(part: String) {
self.part = part
self._exercises = FetchRequest(
entity: Exercise.entity(),
sortDescriptors: [],
predicate: NSPredicate(format: "musclegroup == %#", part as any CVarArg)
)
}
var body: some View {
List(exercises) { e in
Text(e.exercisename)
}
}
}
It's not a good idea to init objects inside View structs because the heap allocation slows things down. You could either have all the predicates created before hand or create one when the picker value changes, e.g. something like this:
// all the Picker samples in the docs tend to use enums.
enum Part: String, Identifiable, CaseIterable {
case abs
case legs
case push
case pull
case fullBody
var id: Self { self }
// Apple sometimes does it like this
// var localizedName: LocalizedStringKey {
// switch self {
// case .abs: return "Abs"
// case .legs: return "Legs"
// case .push: return "Push"
// case .pull: return "Pull"
// case .fullBody: return "Full Body"
// }
// }
}
struct ExerciseListConfig {
var selectedPart: Part = .fullBody {
didSet {
if selectedPart == .fullBody {
predicate = nil
}
else {
// note this will use the lower case string
predicate = NSPredicate(format: "musclegroup == %#", selectedPart.rawValue)
}
}
}
var predicate: NSPredicate?
}
struct ExerciseList: View {
#State private var config = ExerciseListConfig()
var body: some View {
NavigationView {
VStack (alignment: .leading) {
Picker("Body Part", selection: $config.selectedPart) {
//ForEach(Part.allCases) // Apple sometimes does this but means you can't easily change the display order.
Text("Abs").tag(Part.abs)
Text("Legs").tag(Part.legs)
Text("Push").tag(Part.push)
Text("Pull").tag(Part.pull)
Text("Full Body").tag(Part.fullBody)
}.pickerStyle(.segmented)
ExerciseListView(predicate:config.predicate)
}
}
}
}
struct ExerciseListView: View {
// var part: String
let predicate: NSPredicate?
// #FetchRequest var exercises: FetchedResults<Exercise>
init(predicate: NSPredicate?) {
self.predicate = predicate
// self._exercises = FetchRequest(
// entity: Exercise.entity(),
// sortDescriptors: [],
//
// predicate: NSPredicate(format: "musclegroup == %#", part as any CVarArg)
// )
}
var body: some View {
Text(predicate?.description ?? "")
// List(exercises) { e in
// Text(e.exercisename)
// }
}
}
Since you are using Core Data you might want to use an Int enum in the entity for less storage and faster queries.
you could try this simple approach in ListView:
init(part: String) {
self.part = part
self._exercises = FetchRequest(
entity: Exercise.entity(),
sortDescriptors: [],
predicate: (part == "Full Body")
? nil
: NSPredicate(format: "musclegroup == %#", part as any CVarArg)
)
}

iOS DynamoDB Object Mapper load does not return all the attributes

I'm running below function in my iPad app to get an Item by it's hash key (IdName).
This table only contains an Hashkey (No Range key available) but when I run this, It returns an result object which contains only the HashKey Value (The same which I pass). The other property (IdValue) is not there in the result. What am I doing wrong here?
func getCurrentFinalReportNumber()->Int
{
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var currentId :Int = -1
dynamoDBObjectMapper .load(DDBIDStoreTableRow.self, hashKey: "FinalReportNumber", rangeKey: nil) .continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (task:AWSTask!) -> AnyObject! in
if (task.error == nil) {
if (task.result != nil) {
let resultRow :DDBIDStoreTableRow = task.result as! DDBIDStoreTableRow
print(resultRow.IdValue)
currentId = Int(resultRow.IdValue!)
}
} else {
print("Error: \(task.error)")
let alert = SCLAlertView()
alert.addButton("Close", colorButtonBackground: Constants.InspectionTypes.colorClose) {}
alert.showCloseButton = false
alert.showError("", subTitle: (task.error?.description)!)
}
return nil
}).waitUntilFinished()
return currentId
}
class DDBIDStoreTableRow :AWSDynamoDBObjectModel ,AWSDynamoDBModeling {
var IdName:String? //HK
var IdValue:Int? = -1
class func dynamoDBTableName() -> String! {
return AWSIDStoreDynamoDBTableName
}
class func hashKeyAttribute() -> String! {
return "IdName"
}
//MARK: NSObjectProtocol hack
override func isEqual(object: AnyObject?) -> Bool {
return super.isEqual(object)
}
override func `self`() -> Self {
return self
}
}
Just found the mistake. Problem is in the data type of the mapping class. Earlier it was
var IdValue:Int? = -1
But once I change the Int to NSNumber as below. It started to work fine
var IdValue:NSNumber? = -1

Cocoa: react to keyDown in QLPreviewPanel

I implemented quick look in my project in the following way in Swift 2 (I'm including this here for reference and because it might help someone else set it up).
My NSViewController contains a NSTableView subclass where I implemented keyDown to listen to the spacebar key being pressed (maybe not the best way but it works):
override func keyDown(theEvent: NSEvent) {
let s = theEvent.charactersIgnoringModifiers!
let s1 = s.unicodeScalars
let s2 = s1[s1.startIndex].value
let s3 = Int(s2)
if s3 == Int(" ".utf16.first!) {
NSNotificationCenter.defaultCenter().postNotification(NSNotification(name: "MyTableViewSpacebar", object: nil))
return
}
super.keyDown(theEvent)
}
In my view controller, I have an observer for this notification and the functions required by the QLPreviewPanel:
//...
class ViewController: NSViewController {
#IBOutlet weak var myTableView: MyTableView!
var files = [FilesListData]() //array of custom class
//...
override func viewDidLoad() {
//...
NSNotificationCenter.defaultCenter().addObserver(self, selector: "spaceBarKeyDown:", name: "MyTableViewSpacebar", object: nil)
}
func spaceBarKeyDown(notification: NSNotification) {
if let panel = QLPreviewPanel.sharedPreviewPanel() {
panel.makeKeyAndOrderFront(self)
}
}
override func acceptsPreviewPanelControl(panel: QLPreviewPanel!) -> Bool {
return true
}
override func beginPreviewPanelControl(panel: QLPreviewPanel!) {
panel.delegate = self
panel.dataSource = self
}
override func endPreviewPanelControl(panel: QLPreviewPanel!) {
}
}
extension ViewController: QLPreviewPanelDataSource {
func numberOfPreviewItemsInPreviewPanel(panel: QLPreviewPanel!) -> Int {
return self.myTableView.selectedRowIndexes.count
}
func previewPanel(panel: QLPreviewPanel!, previewItemAtIndex index: Int) -> QLPreviewItem! {
if self.myTableView.selectedRow != -1 {
var items = [QLPreviewItem]()
let manager = NSFileManager.defaultManager()
for i in self.myTableView.selectedRowIndexes {
let path = self.files[i].path //path to a MP3 file
if manager.fileExistsAtPath(path) {
items.append(NSURL(fileURLWithPath: path))
} else {
items.append(qm_url) //image of a question mark used as placeholder
}
}
return items[index]
} else {
return qm_url //image of a question mark used as placeholder
}
}
}
What I would like to do now is listen to the keys "up arrow" and "down arrow" being pressed while the quick look panel is open, in order to change the selected row in the NSTableView, much like Finder behaves when you preview files with quick look. I have no clue as to how I could implement this. Any ideas?
Thanks.
Finally found what I was looking for and it's actually pretty simple.
Since my main view controller is also my delegate for the QLPreviewPanel, I added this:
extension ViewController: QLPreviewPanelDelegate {
func previewPanel(panel: QLPreviewPanel!, handleEvent event: NSEvent!) -> Bool {
let kc = event.keyCode
if (kc == 126 || kc == 125) { //up and down arrows
if event.type == NSEventType.KeyDown {
self.myTableView.keyDown(event) //send the event to the table
} else if event.type == NSEventType.KeyUp {
self.myTableView.keyUp(event)
}
return true
}
return false
}
}
Then in my table view delegate:
func tableViewSelectionDidChange(notification: NSNotification) {
guard myTableView.numberOfSelectedRows > 0 else {
if let panel = QLPreviewPanel.sharedPreviewPanel() {
if panel.visible {
panel.close()
}
}
return
}
if let panel = QLPreviewPanel.sharedPreviewPanel() {
if panel.visible {
panel.reloadData()
}
}
}
That's it! The QLPreviewPanelDataSource handles the rest.

Swift: filter protocol array by comparing types

(first post)
usually im able to find answers here or elsewhere but no luck this time =(
Question: in Swift, how do you filter an array that is of a protocol type by an implementing type supplied as a function parameter?
protocol Aprotocol {
var number:Int { get set }
}
class Aclass: Aprotocol {
var number = 1
}
class AnotherClass: Aprotocol {
var number = 1
}
var array:[Aprotocol] = [ Aclass(), AnotherClass(), Aclass() ]
func foo (parameter:Aprotocol) -> Int {
return array.filter({ /* p in p.self == parameter.self */ }).count
}
var bar:Aprotocol = // Aclass() or AnotherClass()
var result:Int = foo(bar) // should return 2 or 1, depending on bar type
maybe this is not the right approach at all?
thanks!
Here is what I think you want:
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == parameter.dynamicType
}.count
But I recommend this, which does the same, but without the useless instance of Aclass() which is passed in the answer on the top. Also this way is faster:
func foo <T: Aprotocol>(type: T.Type) -> Int {
return array.filter { (element: Aprotocol) -> Bool in
element.dynamicType == type
}.count
}
var result:Int = foo(Aclass)
The dynamicType will return the Type of an instance
Very easy:
return array.filter({ parameter.number == $0.number }).count
Kametrixoms solution works (if you use "is T" instead of "== type") but in my case, since i didnt know which implementing class was going to call it, had to go with this solution:
protocol Aprotocol: AnyObject {
var number:Int { get set }
}
class func foo(parameter: AnyObject) -> Int {
return array.filter ({ (element: Aprotocol) -> Bool in
object_getClassName(element) == object_getClassName(parameter)
}).count
}

Resources