runAction(_:completion:) does not show Animation correctly - xcode

I would like to run two actions at the same time and after them another two actions at the same time. Basically I would like to have two groups of action and run them in sequence. I could not manage to group my actions so I used the runAction(_:completion:) function:
self.orangeQuestionmark.runAction(group, completion: {
})
self.blueQuestionmark.runAction(group, completion: {
bubbleArray[0].hidden = false
pony.runAction(ponyMove)
})
This works fine unless this call:
pony.runAction(ponyMove)
With this call I call the animation. See code:
let ponyMove = ponyClass.movingPony("ponyScene3Move1", secondImage: "ponyScene3Move2")
class Pony: SKSpriteNode {
func movingPony(firstImage: String, secondImage: String) ->SKAction{
let movePony = SKAction.animateWithTextures([SKTexture(imageNamed: secondImage), SKTexture(imageNamed: firstImage)], timePerFrame: 0.4, resize: true, restore: false)
moving.append(movePony)
moving.append(SKAction.repeatAction(movePony, count: 6))
let group = SKAction.group(moving)
return group
}
}
However when I run this function outside my runAction(_:completion:) call it works perfectly fine.
Does anyone know what my fault is?

Related

Multiple statements in .onAppear() not working

In my code I am attempting to set two variables in an .onAppear() modifier. The code appears to go into an infinite loop calling the .onAppear() modifier over and over again. Eliminating either one of the two assignments has the expected outcome. Using two .onAppear() modifiers, with one statement each also works as expected (which is my work-around).
import SwiftUI
import PlaygroundSupport
struct ChartView: View {
#State private var isLocked = false
#State private var isOffset = false
let timer = Timer.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
var body: some View {
ZStack {
Text("test")
}
.onAppear(perform: {
self.isLocked = true
//self.isOffset = true
})
.onAppear {
//self.isLocked = true
self.isOffset = true
}
.onAppear {
//self.isLocked = true
//self.isLocked = true
}
.onReceive(timer, perform: { t in
print("Timer fired \(t) with lock \(self.isLocked)")
})
}
}
PlaygroundPage.current.setLiveView(ChartView())
In the code above are three .onAppear() modifiers (in my final code I would have only one), if I have zero or one statement enabled in all three blocks the code executes as expected. If I have any block with both statements enabled the code goes into an infinite loop and the timer never fires.
Using Xcode 12.3 in Playground on an iMac, as well as targeted to an iOS app running on an iPad. Also occurs in the Playground app run on an iPad.
Is this a bug, or am I doing something wrong?
I don't think its an apparent bug, but keep in mind every time #state is update, the view may update as well.
one work around is to use Bool.toggle(), instead of update the value direct.
This way reminds us with a better or careful design only when we need to.
ZStack {
Text("test")
}.onAppear {
if !self.isLocked { self.isLocked.toggle()}
if !self.isOffset { self.isOffset.toggle()}
}.onReceive(timer, perform: { t in
print("Timer fired \(t) with lock \(self.isLocked)")
})

didChangeAutomaticCapitalizationNotification not triggered

What am I doing wrong? I don't get this notification. I have this function:
#objc func onAutocorrection (_ notification: Foundation.Notification) {
Swift.print("\(notification)")
}
later in the same class I do use it as follows:
NotificationCenter.default.addObserver(
self,
selector: #selector(onAutocorrection(_:)),
name: NSSpellChecker.didChangeAutomaticCapitalizationNotification,
object: nil)
The addObserver is executed, but the function is never called even when the application is capitalising in an NSTextView.
Why? Many thanks in advance!
It looks like I misunderstood the notification. It is not meant to be triggered when automatic capitalisation happens but when the systems preference of your Mac is changing.
See the comment of ever helpful Willeke and see Notification of autocorrect
In order to get to the intended result of reacting to autocapitalisation did I implement this function in the NSTextViewDelegate:
public func textView(_ view: NSTextView, didCheckTextIn range: NSRange, types checkingTypes: NSTextCheckingTypes, options: [NSSpellChecker.OptionKey : Any] = [:], results: [NSTextCheckingResult], orthography: NSOrthography, wordCount: Int) -> [NSTextCheckingResult] {
if !range.contains(0){
return results
}
var newResult = [NSTextCheckingResult]()
for result in results {
if let textToChange = view.string[range].components(separatedBy: " ").first, let replacement = result.replacementString?.components(separatedBy: " ").first {
let firstLetterCap = textToChange.capitalizingFirstLetter()
if replacement == firstLetterCap {
continue //don't add to results
}
}
newResult.append(result)
}
return newResult
}
This function will prevent that the first character will be capitalised.
Ultimately, I check whether the capitalised version of the first word of the range that must include position "0" is equal to the first word of the replacement string. And if it is then I remove that result/suggestion from the result list.

Implementing RxSwift with TRON

I am trying to start using RxSwift, therefore I tried to create a function that does a request and I tried to implement the rxResult() function that comes with TRON, the HTTP library I use. But the documentation on this is not very detailed. Can anyone point me in the right direction on what I am doing wrong? This is the function I have written:
static func readAllWithRx() {
let token = UserDefaults.standard.value(forKey: Constants.kTokenUserDefaultsKey) as! String
let url = URL(string: "api/url")!
let request: APIRequest<AssessmentResponse, MyAppError> = APIHelper.tron.request(url.absoluteString)
_ = request.rxResult().subscribe(onNext: { AssessmentResponse in
print("RX AssessmentResponse \(AssessmentResponse)")
}, onError: { Error in
}, onCompleted: {
}, onDisposed: {
})
}
Finally I try to call this request within my Controller using:
let read = Assessments.readAllWithRx()
There’re 2 things at the beginning:
let read = Assessments.readAllWithRx() assumes the function returns something synchronously
Implementation of readAllWithRx you posted doesn’t return anything.
I’ve never used TRON, but as far as I can see, its rxResult() returns an Observable<T> where T is a type of the response. In this case, to get asynchronously AssesmentResponse, you need to subscribe to the observable (as you already did by the way).
Here's an example - an updated implementation of your readAllWithRx (won’t compiled probably, writing code in notepad):
static func readAllWithRx() -> Observable<AssessmentResponse> {
let token = UserDefaults.standard.value(forKey: Constants.kTokenUserDefaultsKey) as! String
let url = URL(string: "api/url")!
let request: APIRequest<AssessmentResponse, MyAppError> = APIHelper.tron.request(url.absoluteString)
return request.rxResult()
}
then, if you need to get AssessmentResponse somewhere in the code:
Assessments.readAllWithRx().subscribe(onNext: { result in
// your response here
print(result)
})

UISwitch: Swift 3: Programmatically

How can I programmatically add a uiswitch and call an action when on and one when off? Ive been searching for hours now. Can I please have some help? I know how to add the switch but it stays on the screen no matter what scene I'm on. So far, I've been able to add the button and make it switch from on to off, but for some reason the switch just says on the screen in every scene. I was lost after that so I followed this; from How to programmatically put a UISwitch in a SpriteKit/Skcene
Yes it is possible. Just use this code in your SKScene class:
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let switchDemo = UISwitch(frame:CGRectMake(150, 300, 0, 0))
switchDemo.on = true
switchDemo.setOn(true, animated: false)
switchDemo.addTarget(self, action: "switchValueDidChange:", forControlEvents: .ValueChanged)
self.view!.addSubview(switchDemo)
}
Helper method:
func switchValueDidChange(sender:UISwitch!)
{
if (sender.on == true){
print("on")
}
else{
print("off")
}
}
I kept getting errors so I did what Xcode suggested which ended up with the SIGBART error.
You are calling the selector wrong on the addTarget action line. They finally changed it at one point in Swift 2 to get rid of using strings for selector method calls, which now makes them a lot less error prone.
Change it to this (Swift 3 syntax)
switchDemo.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged)
You basically call #selector in the action parameter and include the method you want to call, in your case switchValueDidChange. Note the (_:) syntax at the end, thats indicating that the method you want to call takes a parameter, in your case a UISwitch.
func switchValueDidChange(_ sender: UISwitch) {
...
}
If you want to call a regular method that takes no parameters e.g
func switchValueDidChange() {
}
than you would just say
switchDemo.addTarget(self, action: #selector(switchValueDidChange), for: .valueChanged)
without the (_:) syntax.
Hope this helps
Updated to swift 5
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let switchDemo = UISwitch(frame:CGRect(x: 150, y: 300, width: 0, height: 0))
switchDemo.isOn = true
switchDemo.setOn(true, animated: false)
switchDemo.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged)
self.view!.addSubview(switchDemo)
}
Helper method:
#objc func switchValueDidChange(_ sender: UISwitch!) {
if (sender.isOn){
print("on")
}
else{
print("off")
}
}
With parameter:
#IBOutlet var categorySwitch:UISwitch!
var categorySwitchIsOn:Bool = false
On viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
categorySwitch.addTarget(self, action:#selector(ViewController.categorySwitchValueChanged(_:)), for: .valueChanged)
}
Associated function:
func categorySwitchValueChanged(_ sender : UISwitch!){
if sender.isOn {
categorySwitchIsOn = true
} else {
categorySwitchIsOn = false
}
}

Swift: Error updating UI component when using a delegate

I'm trying to update a progress bar with the progress of loading a load of values into CoreData. However, whenever I try to call an update on my progressView component, I get a fatal error stating that "unexpectedly found nil while unwrapping an Optional value".
The interesting thing is that this happens even if I put 'self.progressView.progress = 0.5' in the delegate method of my program - indicating that it's the progressView component it can't find rather than an issue with the value. A quick check with println also confirms the value does exist and so isn't nil. Note that if I put the 'self.progressView.progress = 0.5' statement under a function connected directly to a button, it works fine so it must be some sort of issue with the command being called from the delegate.
Can anyone work out what I'm doing wrong here? Thanks for your help.
Delegate method:
class ViewControllerUpdate: UIViewController, NSURLSessionDelegate, NSURLSessionDownloadDelegate, saveUpdate {
[....]
func updateStatus(status: String, progress: Float?) {
if let percentProgress = progress? {
self.progressView.progress = 0.5
}
//println(progress) - NOTE THIS IS CORRECTLY POPULATED WITH THE APPROPRIATE VALUE
}
Calling class:
protocol saveUpdate {
func updateStatus(status:String, progress:Float?)
}
class sqlPullSave {
let classtoUpdate: saveUpdate = ViewControllerUpdate()
func saveTSVtoSQL(fromFile: NSURL) -> Int {
//Load up the information into a Dictionary (tsv)
//let tsvURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(fromFileName, ofType: fromFileExtension)!)
let tsvURL: NSURL = fromFile
let tab = NSCharacterSet(charactersInString: "\t")
let tsv = CSV(contentsOfURL: tsvURL, separator: tab)
//let defResult: AnyObject = tsv.rows[0]["Name"]!
//let tryagain:String = AnyObjecttoString(tsv.rows[1]["Name"]!)
//load the data into the SQLite database...
dispatch_async(dispatch_get_main_queue()) {
for a in 0..<tsv.rows.count {
self.SQLsaveLine(self.AnyObjecttoString(tsv.rows[a]["Name"]!),
name_l: "",
desc: self.AnyObjecttoString(tsv.rows[a]["1"]!),
jobTitle: self.AnyObjecttoString(tsv.rows[a]["2"]!),
extn: self.AnyObjecttoString(tsv.rows[a]["3"]!)
// update status
var percentComplete: Float = (Float(a) / Float(tsv.rows.count))
self.classtoUpdate.self.updateStatus("SQLload", progress: percentComplete)
}
}
return 0
}

Resources