I have an options ViewController where the user can change the password and a pickerView. Whenever he presses the save button a timer starts and the user gets directed to the main screen through a segue. I want to be able to grey the pickerView out for a certain amount of time (on the options ViewController) as soon as the user presses this save button (it equals a time-based restriction).
My problem now is that anytime the user presses the button and gets directed to the main screen, the pickerview.alpha on the options ViewController (which i set for the grey out effect) will not change / the change is not visible when the user returns in the time period. Also the pickerview.alpha is not changing back on the options ViewController screen when the time period expieres.
So in order to follow my describtion here is my code so far:
import UIKit
class OptionsViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate
#IBOutlet var PickerView: UIPickerView!
var seconds = 20
var timer = Timer()
var isTimerRunning = false
#IBAction func Save_Touched(_ sender: Any) {
if isTimerRunning == false {
runTimer()
}
self.performSegue(withIdentifier: "fromOptionstoMainSegue", sender: self)
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(OptionsViewController.updateTimer), userInfo: nil, repeats: true)
self.PickerView.alpha = 0.5
isTimerRunning = true
}
func updateTimer() {
if seconds < 1 {
timer.invalidate()
self.PickerView.alpha = 1.0
} else {
seconds -= 1
print(seconds)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.PickerView.dataSource = self
self.PickerView.delegate = self
print(isTimerRunning)
}
}
I tried endless options in order to achieve it, but could not make it.
Therefore i am really looking forward to any answer or help!
With kind regards
You can easily disable the pickerView. Either hide it completely:
PickerView.isHidden = true
then unhide it later, or just simply disable it:
PickerView.isUserInteractionEnabled = false
and enable it again after your save is finished. There is no need to grey out the pickerView.
EDIT:
There is nothing wrong with your code for setting the alpha of your picker view. Make sure your outlet is linked to the picker view in the storyboard.
Related
I'm creating a popover style view controller in storyboard like this
Then I click the button, the view controller shows and when I click anywhere outside, the view controller is "dismissed".
However, when I click the button again, a new instance of the view controller is lunched and the previous one is still running. I have tried deinit but it's not getting called when the view controller is "dismissed".
How can I either destroy the view controller instance when clicking outside, or "show" the already created instance?
My code in the view controller:
class FileTransViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
//print(123)
print("\(self)")
}
deinit {
print("destroyed")
if let timer = timer {
timer.invalidate()
}
}
#objc func updateProgress() {
print("updating progress")
}
}
The problem has nothing to do with popovers. You are leaking because you retain the timer while the timer retains you — a classic retain cycle.
To break the cycle, you must invalidate the timer. You cannot do this in deinit, because by definition it cannot be called until after you break the cycle. NSPopover.willCloseNotification might be a good opportunity.
As #matt says, you have problem with retain cycle. You can avoid it using Timer with block where you can declare weak reference for self
timer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
print("updating progress")
}
You also don't need deinit in this case since you're invalidating timer inside guard's else block and you also don't need variable timer and you can just write Timer if you don't want to invalidate timer manually somewhere else
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(...) { ... }
}
I am having a weird issue with a button. So I have a NSViewController with many subviews in it. When I click a button, a new NSView with click gestures and buttons is added on top. But I can't press any of them, they don't respond unless a click for 2 seconds and then release. I've tried disabling the gestures of the holder but it didn't work. Any suggestions?
Well, some of the rest of us do. In my case, it's for buttons on a view in a sheet, so "many subviews" isn't likely it. My view controller for the sheet is about 100 lines. Still debugging...
At present the VC is as follows. The snp.makeConstraints calls are for SnapKit (from GitHub)
#objc
class ThreadEditSheetViewController: NSViewController {
/// The container for the graphics view
#IBOutlet var sheetView: NSView!
/// The information packet initialized by the invoking view controller
var info: ThreadEditInfo!
/// API
override func viewDidLoad() {
super.viewDidLoad()
}
/// API
override func viewWillAppear() {
guard let gvc = (try? self.bundleLoader(id: "GraphicsViewController")) as? GraphicsViewController else {
fatalUserAlert(error: AppError.UIConstructionFailure, message: "Can't find GraphicsViewController for ThreadEditSheetViewController")}
let gv = gvc.view
self.view.addSubview(gv)
// Spaces in title text move it left to avoid visual overlap with scroll bar. Don't know how to do it with
// constraints given the scrolling view
let done = makeButton(gvc: gvc, title: "done ", action: #selector(doneEditing(_:)))
done.snp.makeConstraints{ (make) in
make.top.equalTo(gv).offset(-5)
make.right.equalTo(gv).offset(-5)
}
let cancel = makeButton(gvc: gvc, title: "cancel", action: #selector(cancelEditing(_:)))
cancel.snp.makeConstraints{ (make) in
make.top.equalTo(gv).offset(-5)
make.left.equalTo(gv).offset(5)
}
self.view.becomeFirstResponder()
super.viewWillAppear()
return
}
func makeButton(gvc: NSViewController, title: String, action: Selector) -> NSButton {
let button = NSButton(title: title, target: self, action: action)
let gv = gvc.view
gv.addSubview(button)
button.backgroundColor = .clear
button.setButtonType(.momentaryChange)
button.isTransparent = true
return button
}
#objc
func doneEditing(_ sender: Any) {
self.dismissViewController(self)
}
#objc
func cancelEditing(_ sender: Any) {
self.dismissViewController(self)
}
}
I have 2 view controllers which should be swapped according to userinput. So, I want to switch the views programatically based on the input I get from a text file.
Algorithm :
if(input == 1)
{
Go to View Controller 1
}
else if(input ==2)
{
Go to View Controller 2
}
Any help on how to click the button programmatically or load that particular viewcontroller with input?
To fire an event programmatically you need to call sendActionsForControlEvent
button.sendActionsForControlEvents(.TouchUpInside)
--
Swift 3
button.sendActions(for: .touchUpInside)
Or you can just put all the logic that you perform when a button gets clicked in a separate method, and call that method from your button's selector method.
#IBAction func someButtonPressed(button: UIButton) {
pushViewControllerOne()
}
#IBAction func someButtonPressed(button: UIButton) {
pushViewControllerTwo()
}
func pushViewControllerOne() {
let viewController = ViewControllerOne(nibName: "ViewControllerOne", bundle: nil)
pushViewController(viewController)
}
func pushViewControllerTwo() {
let viewController = ViewControllerOne(nibName: "ViewControllerTwo", bundle: nil)
pushViewController(viewController)
}
func pushViewController(viewController: UIViewController) {
navigationController?.pushViewController(viewController, animated: true)
}
Then instead of invoking programatically invoking a button press, just call the method pushViewControllerOne() or pushViewControllerTwo()
I try to show a UIView when a button is clicked, but it seems that the UI can't get updated, because the button also activates a segue. When I delete the segue, the UI is updated and my UIView is drawn.
Can anyone help me on how to solve this problem?
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject!) -> Bool {
if identifier == "mySegue" {
if(myLocation == nil) {
/* Rest of code */
return false
}
else {
// These properties need to be set
box.hidden = false
actInd.startAnimating()
return true
}
}
return true
}
#IBAction func startComputing(sender: UIButton) {
//When I put the properties, nothing happens,
//but something like print("Button pressed") does happen.
}
Thanks in advance!
From what I understand, the view is within the view controller window hierarchy, so I'd do this without using segues:
#IBAction func startComputing(sender: UIButton) {
UIView.transitionWithView(self.yourView, duration: 0.5, options:UIViewAnimationOptions.TransitionFlipFromLeft, animations: {
}, completion: nil)
}
This will present your view when the button is clicked. Inside the block you can do the rest like updating UI etc.
I've got now an app that if you click on a button it will disappear, now I want to make the button disappear even when your not clicking on that button, but it will come back in a few seconds (or even less than a second). On the moment this is how the button looks in the code.
#IBAction func increaseCount(button: UIButton) -> Void {
button.hidden = true
ourScore.text = "\(++score)"
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(Double((arc4random_uniform(1) + 2)) * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
button.hidden = false
}
}
How is it possible to make the button disappear even when your not clicking it, but it will come back in just a few seconds (and less than a second) back? The time should be random between 2 and a half second. When your clicking it should also disappear and it will come back less than 2 seconds.
Who could help me?
This code will have the button appear and reappear on its own every 2 seconds. You can modify the time so it is random (if you need help with that, let me know).
Link your button on the story board and the code below should do the trick.
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.hidden = true
NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "appear:", userInfo: self, repeats: false)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func appear(timer: NSTimer) {
self.button.hidden = true
NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "disappear:", userInfo: self, repeats: false)
}
func disappear(timer: NSTimer) {
self.button.hidden = false
NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "appear:", userInfo: self, repeats: false)
}
EDIT: For the button to disappear when clicked, register an Action event from the button and use the code:
#IBAction func clicked(sender: UIButton) {
self.button.hidden = true
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "appear:", userInfo: self, repeats: false)
}
Again, this hides it just for 1 second when it is clicked, but you can change the time to be random.
Edit 2: You should see this: