Xcode delayed button - ios

Hello I'm new here and new to app development but I'm building an app with multiple views and I would like to add a button that will go to the next view but the user will have to wait 3 seconds or so on each view before the button is available to be tapped.

You could perform a method after a delay with reveals or enables your button,
[self performSelector:#selector(yourMethod) withObject:nil afterDelay:3.0];

if you want the delay to happen after button tap then have a look at the below code
let tapGesture = UITapGestureRecognizer(target: self, action: "tapAction")
tapGesture.numberOfTapsRequired = 1
Your_Button_name.addGestureRecognizer(tapGesture)
#IBAction func tapAction()
{
Your_Button_name.disable=true
//Delay function to enable your button
NSTimer.scheduledTimerWithTimeInterval(Your_time_value_delay, target: self, selector: Selector("enablefunc"), userInfo: nil, repeats: false)
// DO SOMETHING WHICH YOU WANT TO DO.....
}
func enablefunc()
{
Your_Button_name.disable=false
}
Hope this helps. You can modify it based on your requirement....

Related

Strange Behavior with UITapGestureRecognizer

I implemented a SnackBar / Toast Message in Swift 4 which allows the developer to set the following display duration (enum) and with the following behaviour:
.long / .short: Timer gets fired and dismisses the SnackBar OR the user taps the view and it also dismisses
.constant: No Timer, only TapGesture to dismiss
Now in the first case (.long / .short) the SnackBar reacts as expected. Either the Timer fires or the user taps and the view gets dismissed.
In the second case (.constant) - where there is no timer - the Tap gesture is not recognised and the view doesn't get dismissed
here are the three functions:
private func defineDismissMode(){
switch self.message.duration {
case .short, .long:
self.setTimer()
case .constant:
self.setTapGesture()
}
}
private func setTimer(){
guard self.timer == nil else {return}
self.timer = Timer.scheduledTimer(timeInterval: self.message.duration.rawValue, target: self, selector: #selector(self.dismissSnackBar), userInfo: nil, repeats: false)
self.setTapGesture()
}
private func setTapGesture() {
self.tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissSnackBar))
self.snackView.addGestureRecognizer(self.tapGestureRecognizer!)
}
#objc private func dismissSnackBar(){
...
}
Anyone a clue why when setTapGesture() is called after setTimer() it works and a tap triggers self.dismissSnackBar , but when called without setTimer() the UITapGestureRecognizer is not triggered?
Note: Yes I checked that case: .constant -> setTapGesture() gets called

Modifying keyboard toolbar / accessory view with WKWebView

I'm using a WKWebView with a content-editable div as the core of a rich-text editor, and would like to modify the top toolbar above the keyboard—the one that contains the autocorrect suggestions and formatting buttons. (Not sure if this counts as an input accessory view or not).
I've found a few posts showing how to remove the bar, but none of them seems to work, and ideally I'd like to keep the autocorrect part anyway.
At least one app, Ulysses, does this (though I don't know if it's with a web view):
And indeed, I'm pretty sure I can achieve it by doing surgery on the keyboard view hierarchy...but that seems like a tedious and brittle approach.
Is there a better way?
Thanks!
Maybe this tutorial on UITextInputAssistantItem would be helpful.
That said, after fiddling around with this for a while, using WKWebView I still could only get this to work for the first time the keyboard displayed, and every time after that it would return to its original state. The only thing I found that consistently worked was something like the following:
class ViewController: UIViewController {
#IBOutlet weak private var webView: WKWebView!
private var contentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
webView.loadHTMLString("<html><body><div contenteditable='true'></div></body></html>", baseURL: nil)
for subview in webView.scrollView.subviews {
if subview.classForCoder.description() == "WKContentView" {
contentView = subview
}
}
inputAssistantItem.leadingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed(_:)))], representativeItem: nil)]
inputAssistantItem.trailingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .camera, target: self, action: #selector(openCamera(_:))), UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(actionPressed(_:)))], representativeItem: nil)]
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: .UIKeyboardDidShow, object: nil)
}
#objc private func donePressed(_ sender: UIBarButtonItem) {
view.endEditing(true)
}
#objc private func openCamera(_ sender: UIBarButtonItem) {
print("camera pressed")
}
#objc private func actionPressed(_ sender: UIBarButtonItem) {
print("action pressed")
}
#objc private func keyboardDidShow() {
contentView?.inputAssistantItem.leadingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed(_:)))], representativeItem: nil)]
contentView?.inputAssistantItem.trailingBarButtonGroups = [UIBarButtonItemGroup(barButtonItems: [UIBarButtonItem(barButtonSystemItem: .camera, target: self, action: #selector(openCamera(_:))), UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(actionPressed(_:)))], representativeItem: nil)]
}
}
It will give something like this:
It's a bit frustrating to have to set this on the content view every time the keyboard shows, as well as on the view controller itself, and I hope there's a better way to do this.... But unfortunately I could not find it.
Perhaps a more stable (future-proof) way of having the same behavior would be to place the input accessory view in the WKWebView.superview, then use respond to keyboard events, to show and hide it. It's the old-fashioned way of making input accessory views.
Details:
observe keyboard show/hide notifications
place the input accessory view in the WKWebView.superview
on keyboard show: position the input accessory view above the keyboard
on keyboard hide: hide it with keyboard
And perhaps obvious, use delegation to pass action events between the WKWebView and your UIViewController/UIView subclass.
Also, in iOS 13+, you can override the inputAccessoryView. You should be able to replace it to provide your own on iPad. More details here: https://stackoverflow.com/a/58001395/344591

NSTimer to Change Text

Sorry if this is a really basic questions, but I can’t seem to work it out, so I thought I would ask the experts.
I’ve got a timer for my project that counts down and updates a label according to what is stored in an array.
var array : String[]()
var x = 0
#IBAction func playBtnPressed(sender: UIButton)
{
timer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: #selector(PlayVC.update), userInfo: nil, repeats: true)
}
func update()
{
if x < array.count {
let item = array[x]
aLbl.text = array.itemTitle
x += 1
}
}
My problem is that the text is only updated after the first countdown and 60 seconds is a long time to wait lol.
I would actually like the first String in my array to appear as soon as the button is tapped.
Is there a way to set the text at the very beginning of the countdown?
Thank you for your help :)
So you want to update a label every minute. And you also want to update it immediately after the button is pressed. Hopefully I didn't misunderstand the question.
It's actually as easy as adding this line before the timer = NSTimer ... line:
update()
Note that your current code can cause two or more timers to be created and run when the button is pressed more than once. You might not want this.
To stop the timer when the button is pressed a second time, do this:
if timer == nil {
timer = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: #selector(PlayVC.update), userInfo: nil, repeats: true)
} else {
timer.invalidate()
timer = nil
}
To do nothing when the button is pressed a second time jus remove the else part.

Swift - how to run multiply timers?

In my app I have a view, and I want this view to be dismissed everytime it is shown after 8 seconds. In order to do that I created a timer inside view creation function that hides view after firing. It works. But in some cases I want to present two copies of the view I want both be dismissed after time it was created +8 seconds. Issue is that timer works only once - it hides one copy of view, and second remains on screen
My code is
func presentShowStatusShort(text text:String) {
pointsShort = NSBundle.mainBundle().loadNibNamed("helperShowStatusShort", owner: self, options: nil).first! as! helperShowStatusShort
pointsShort?.frame.size.width = self.view.bounds.width
if pointsShortOnScreen {
pointsShort!.frame.origin.y = pointsShort!.frame.origin.y + 39
}
pointsShort?.text.text = "\(text.uppercaseString)"
pointsShort!.dismissButton.addTarget(self, action: "dismissShowStatusShort", forControlEvents: UIControlEvents.TouchUpInside)
var timer = NSTimer.scheduledTimerWithTimeInterval(8, target: self, selector: "dismissShowStatusShort", userInfo: nil, repeats: false)
pointsShortOnScreen = true
view.addSubview(pointsShort!)
}
func dismissShowStatusShort() {
pointsShortOnScreen = false
UIView.animateWithDuration(0.5, animations: {
self.pointsShort?.frame.origin.y = (self.pointsShort?.frame.origin.y)! - 64
}, completion: {
finished in
self.pointsShort?.removeFromSuperview()
})
}
From an OOP point of view you should create a class to help you with this. The class would own the timer and maintain a reference to the view. When the timer expires it calls a callback, passing itself as the parameter, so the controller can get the associated view and dismiss it.
All of the instances of this class would be stored in an array instance variable on the controller so you can add multiple and remove each one once it expires.

Progress for LongPressureGesture on a Button

I got a button with a LongPressureGesture, and i would like to have a small ProgressView on top of this button as visual feedback for the user that the longPressureGesture is recognized.
I'm stuck on how to detect the beginning of the longPressure and the duration of the longPressure to be able to set the setProgress() on my ProgressView.
EDIT: So i inspired myself from the answers, thank you. Here is what i made. Feel free to comment the following code, maybe there is a better solution.
private var lpProgress:Float = 0
private var startTouch: NSTimer!
#IBAction func pauseJogButtonTouchDown(sender: AnyObject) {
self.startTouch = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "updateLPGestureProgressView", userInfo: nil, repeats: true)
}
func updateLPGestureProgressView() {
self.lpProgress += 0.1
self.lpGestureProgressView.setProgress(self.lpProgress, animated: true)
if self.lpProgress >= 1 {
self.startTouch.invalidate()
self.pauseBarButton.hidden = true
self.lpGestureProgressView.setProgress(0.0, animated: false)
self.toolbarHomeMadeView.hidden = false
self.switchToState(.Paused)
}
}
#IBAction func pauseJogButtonTouchUpInside(sender: AnyObject) {
self.lpProgress = 0
self.startTouch.invalidate()
self.lpGestureProgressView.setProgress(0.0, animated: false)
}
You do not need the LongPressureGesture in this case.
Use "Touch Down" IBAction of UIButton to start NSTimer, and "Touch Up Inside" to stop timer and check if the delay was right.
ProgressView you can fill by timer progress.
Set up an NSTimer on touchesBegan.
At the same time start your animation to animate the view.
When touchesEnded is triggered then stop the animation if the NSTimer has not triggered yet and cancel the timer.
When the timer finishes run your desired action.
Long Press isn't really designed for this sort of thing.

Resources