I am currently learning Swift and can not continue. I create a dice game. If the "if query" is true, the program should pause briefly before a new query is possible.
#IBAction func guess(_ sender: Any) {
let diceRoll = arc4random_uniform(5)
let fingerErgebnis = diceRoll + 1
let fingerAntwort = String(fingerErgebnis)
if fingerTextfield.text == fingerAntwort{
ergebnis.text = "Richtig erraten!"
ergebnis.textColor = UIColor.green
ergebnis.font = ergebnis.font.withSize(20)
bild.image = UIImage(named: ("win.jpg"))
button.setTitle("Neues Spiel!", for: .normal)
fingerTextfield.text = " "
sleep (2)
}else if fingerErgebnis == 1 {
ergebnis.text = "Leider falsch! Die Zahl war \(fingerAntwort)."
ergebnis.textColor = UIColor.red
bild.image = UIImage(named: ("finger1.jpg"))
button.setTitle("Versuch es nochmal!", for: .normal)
} ...
As far as everything works, but I want that everything is running first and I have to wait 2 seconds until I can click the button again. My test is paused first, then the rest of the if commands are executed. I want that the other way around.
Sorry for my terrible english ;)
You can disable your button and re-enable it after two seconds using async(deadline:execute:). So, instead of sleep(2) put the following:
button.isUserInteractionEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
// assuming button is a property of the current view controller
self?.button.isUserInteractionEnabled = true
}
sleep (2)
This is the wrong way to create the delay you're looking for. sleep() stops your process entirely, at least on that thread. What you should do instead is to disable the button and then use a Timer (or some other form of delayed execution, but Timer is easy) to re-enable it two seconds later.
Related
My solution is hacky and frankly doesn't even work because the spin loop halts other simultaneous button actions from being recognized. I'd love to be able to use a long press gesture recognizer, but that 's not available on GCController.
let dPadLeftButtonPressedChangedHandler = {
(dPadLeftButton: GCControllerButtonInput, value: Float, pressed: Bool) -> Void in
if dPadLeftButton.isPressed {
// No longer nil
self.moveLeft()
self.nanoTimer = DispatchTime.now()
while (dPadLeftButton.isPressed) {
let nanoTimerNow = DispatchTime.now()
// Thousandths of a second
let timeDifference = (nanoTimerNow.uptimeNanoseconds - self.nanoTimer!.uptimeNanoseconds) / UInt64(1000000)
// 200 / 1000 is .2 seconds
if timeDifference > 250 {
self.hardLeft()
self.nanoTimer = nil
break
}
}
}
}
controller?.extendedGamepad?.dpad.left.pressedChangedHandler = dPadLeftButtonPressedChangedHandler
I solved this by initialzing a CADisplayLink object to call the function I want repeated via the selector argument. Then on the button release, I invalidate the CADisplayLink.
I am trying to make a simple game with Xcode 11.2 which contains an animated loop background and a view which shows and changes various animations from several Lottie JSON files in project.
When I click the "Next" button to change the view animation, background view which is looping gets stuck for a second until the next animation inside the view is loaded an everything in the app freezes at the same time just like the background animation.
CPU usage varies from 30% to 63%.
I don't like to complicate the question, so I am just showing the way I used Lottie.
#IBAction func SubmitButtonAction(_ sender: UIButton) {
showNextQuestion()
}
func showNextQuestion()->(){
myTimer.invalidate()
startCountdown(fromTime: 15)
Manager.generateQuestion()
lblLevel.text = String(Manager.questionNumber) + "/" + String(DataModel.Questions.count)
nIndex = 0
let animation = AnimationView(name: Manager.currentImage)
animation.loopMode = .loop
animation.play()
animation.backgroundColor = UIColor.clear
animation.frame = self.AnimView.bounds
animation.backgroundBehavior = .pauseAndRestore
if AnimView.subviews.isEmpty{
self.AnimView.addSubview(animation)
}
else {
for one in imgImageView.subviews{
one.removeFromSuperview()
}
self.AnimView.addSubview(animation)
}
AnswerCollectionView.reloadData()
RandomCollectionView.reloadData()
}
I am interested in suggestions as to what the problem is related to - could it be related to a threading issue?
In response to Jaseel.Dev, I created a function that returned a LottieView with the following:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation(.spring()) {
LottieView(name: animation)
}
}
When I taped button UILabel appears and immediately disappears again. I need it to disappear after a few seconds. It's my first app and I can't solve this problem.
Thanks!
func done() {
if sauserImageView.isHidden == false && cupImageView.isHidden == false && spoonImageView.isHidden == false {
winningLabel.isHidden = false
}
}
You can perform a delayed action by using the DispatchQueue API, e.g.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.label.isHidden = true
}
Or if you want to animate the hiding, use UIView.animate(withDuration:animations:) or UIView.animate(withDuration:delay:options:animations:completion:) e.g.:
UIView.animate(withDuration: 2) {
self.label.alpha = 0
}
Good luck!
I am working on UITests using XCode. I have multiple CollectionView cells.
When I perform Count in the collectionView it shows the certain count.
I can able to access first two cells but coming to the 3rd cell as 3(depends on device size). It says that specific button I am looking for in 3rd Cell as exists.
But isHittable is false.
Is there any way I can tap on the button on the 3rd Cell.
I have tried using the extension for forceTapElement() which is available online, it didn’t help.
Extension Used:
extension XCUIElement{
func forceTapElement(){
if self.isHittable{
self.tap()
}else{
let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: .zero)
coordinate.tap()
}
}
}
Tried to perform swipeUp() and access the button. it still shows isHittable as false
The only way I've found is to swipe up untile the isHittable will be true.
app.collectionViews.cells.staticTexts["TEST"].tap()
Thread.sleep(forTimeInterval: 3)
let collectionView = app.otherElements.collectionViews.element(boundBy: 0)
let testAds = collectionView.cells
let numberOfTestAds = testAds.count
if numberOfTestAds > 0 {
let tester = collectionView.cells.element(boundBy: 2).buttons["ABC"]
for _ in 0..<100 {
guard !tester.isHittable else {
break;
}
collectionView.swipeUp()
}
}
Please note that the swipeUp() method will only move few pixels. If you want to use more comprehensive methods you can get AutoMate library and try swipe(to:untilVisible:times:avoid:from:):
app.collectionViews.cells.staticTexts["TEST"].tap()
Thread.sleep(forTimeInterval: 3)
let collectionView = app.otherElements.collectionViews.element(boundBy: 0)
let testAds = collectionView.cells
let numberOfTestAds = testAds.count
if numberOfTestAds > 0 {
let tester = collectionView.cells.element(boundBy: 2).buttons["ABC"]
collectionView.swipe(to: .down, untilVisible: tester)
// or swipe max 100 times in case 10 times is not enough
// collectionView.swipe(to: .down, untilVisible: tester, times: 100)
}
I am trying to take a picture every 2 seconds by using a while loop. but when I try this the screen freezes.
This is the function that takes the photo:
func didPressTakePhoto(){
if let videoConnection = stillImageOutput?.connectionWithMediaType(AVMediaTypeVideo){
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {
(sampleBuffer, error) in
if sampleBuffer != nil {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let dataProvider = CGDataProviderCreateWithCFData(imageData)
let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, .RenderingIntentDefault)
let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
//Adds every image taken to an array each time the while loop loops which will then be used to create a timelapse.
self.images.append(image)
}
})
}
}
To take the picture I have a button which will use this function in a while loop when a variable called count is equal to 0, but when the end button is pressed, this variable is equal to 1, so the while loop ends.
This is what the startPictureButton action looks like:
#IBAction func TakeScreanshotClick(sender: AnyObject) {
TipsView.hidden = true
XBtnTips.hidden = true
self.takePictureBtn.hidden = true
self.stopBtn.hidden = false
controls.hidden = true
ExitBtn.hidden = true
PressedLbl.text = "Started"
print("started")
while count == 0{
didPressTakePhoto()
print(images)
pressed = pressed + 1
PressedLbl.text = "\(pressed)"
print(pressed)
sleep(2)
}
}
But when I run this and start the timelapse the screen looks frozen.
Does anyone know how to stop the freeze from happening - but also to add each image taken to an array - so that I can turn that into a video?
The problem is that the method that processes clicks on the button (TakeScreanshotClick method) is run on the UI thread. So, if this method never exits, the UI thread gets stuck in it, and the UI freezes.
In order to avoid it, you can run your loop on the background thread (read about NSOperation and NSOperationQueue). Occasionally you might need to dispatch something from the background thread to the UI thread (for instance, commands for UI updates).
UPDATE: Apple has a really great documentation (best of what I've seen so far). Have a look at this: Apple Concurrency Programming Guide.
You are calling the sleep command on the main UI thread, thus freezing all other activity.
Also, I can't see where you set count = 1? Wouldn't the while loop continue forever?