I'm new to iOS development, but I'm having an issue with one of the views that I'm working on. I have a UIDatePicker that can either be hidden or visible depending on the state of a UISwitch. It seems that the associated #IBAction does not trigger when the view starts out hidden. It does work when the date picker starts out visible, so the IBAction is working.
Here's a simplified version of my code:
import UIKit
class StatusEditorViewController: UIViewController {
#IBOutlet var expiryPicker: UIDatePicker!
#IBOutlet var enableExpirySwitch: UISwitch!
var editingObject: StoredStatus?
private var pickerIsVisible = false
private var expiresIn: TimeInterval?
override func viewDidLoad() {
// Set a default value
expiryPicker.countDownDuration = TimeInterval(3600)
// If this view got passed an object to edit, use that for expiresIn
if let status = editingObject {
if let expires = status.expiresIn.value {
expiresIn = TimeInterval(expires)
}
}
// Hide the picker and turn off the "enable expiry" switch if we don't
// have a value yet. We'll show the picker once the switch has been pressed
pickerIsVisible = expiresIn != nil
enableExpirySwitch.isOn = expiresIn != nil
updatePicker()
}
func updatePicker() {
expiryPicker?.isHidden = !pickerIsVisible
}
#IBAction func expiryDidUpdate(_ sender: UIDatePicker) {
expiresIn = sender.countDownDuration
print(expiresIn!)
}
#IBAction func expirySwitchDidUpdate(_ sender: UISwitch) {
pickerIsVisible = sender.isOn
updatePicker()
// If the user just turned on the switch, we want to make sure we store the
// initial value already, in case the user navigated away
if (sender.isOn && expiresIn == nil) {
expiresIn = expiryPicker.countDownDuration
}
}
}
I'm not sure what's going wrong. I tried manually attaching a target (e.g. self.expiryPicker.addTarget(self, action: #selector(setExpiryValue), for: .allEditingEvents))
once the view becomes available, but that didn't work either.
I hope someone can tell me what I'm doing wrong. I'm guessing there's something fundamental that I'm doing wrong, but so far no search on Google or SO has led me to the answer.
Thanks in advance
f.w.i.w, I'm running XCode 11.7, with Swift 5, with a deployment target of iOS 13.7
I have created a menu app, using Swift, for Mac OS, within which, a custom view is the only menu item. There's a plus button on this custom view, which opens a window that has a textfield.
When I click on the plus button, the window appears, but the menu does not disappear. The textfield is also not focused. When I type one letter, the letter is not shown in the textfield, but the menu disappears, and the textfield is focused and ready to receive entry.
I want to have the custom view or menu disappear and have the textfield ready to receive keystrokes when I click on the plus button, not after I press an extra key.
How may I achieve that? What am I doing wrong?
Here's my code:
// CustomView.swift
var customWindow: CustomWindow!
override func awakeFromNib() {
customWindow = CustomWindow()
}
#IBAction func plusButtonClicked(_ sender: NSButton) {
customWindow.showWindow(nil)
}
// CustomWindow.swift
override var windowNibName : NSNib.Name? {
return NSNib.Name("CustomWindow")
}
override func windowDidLoad() {
super.windowDidLoad()
self.window?.center()
self.window?.makeKeyAndOrderFront(self)
self.window?.level = .mainMenu + 100
NSApp.activate(ignoringOtherApps: true)
if customTextField.acceptsFirstResponder {
customTextField.window?.makeFirstResponder(customTextField)
}
// CustomMenuContoller.swift
let statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
#IBOutlet weak var CustomMenu: NSMenu!
#IBOutlet weak var customView: CustomView!
var customMenuItem: NSMenuItem!
override func awakeFromNib() {
customMenuItem = CustomMenu.item(withTitle: "TheMenu")
customMenuItem.view = customView
statusBarItem.menu = CustomMenu
}
Inspired by El Tomato's comment, I found the solution.
Given the fact that the plusButtonClicked is limited to its own context, which is the controller within which it resides and all the public variables, I could not call a method on CustomMenu from it. Because CustomMenu in itself is not public. But its containing variable statusBarItem.menu, is public and accessible from all the other views. So I added statusBarItem.menu?.cancelTracking() to plusButtonClicked action and it works.
How do i set the maximum amount of characters in multiple NSTextfields (OSX cocoa app, NOT iOS) to one in Swift?
Please explain how to do it, because I'm a complete newbie when it comes to OSX app development and therefore I don't understand short answers like "Use NSFormatter", because I have no idea what it is and how to implement it. Like Examples
There's no built-in way to simply set the maximum, I think because you need to decide what behavior you want. For example, if there's already one character in the field, and the user enters a second character, what should happen? Should the 2nd character be ignored? Replace the first? Beep?
In any case, you can get whatever behavior you want using the NSText Delegate methods. Make your view controller (or whatever object has the logic) a delegate of the text field, and implement the various delegate method(s) to do what you need.
Again, the exact behavior is up to you, but if I were implementing this, I might be inclined to make the text field always use the last character entered (such that, if one character is already present, pressing a second replaces the first). To do that, you'd want to override textDidChange to look at the value of the text field, and modify it if appropriate. Something like:
class ViewController: NSViewController, NSTextFieldDelegate {
#IBOutlet weak var textField: NSTextField!
override func controlTextDidChange(obj: NSNotification) {
if self.textField.stringValue.characters.count > 1 {
self.textField.stringValue = String(self.textField.stringValue.characters.last!)
}
}
}
You don't need to limit the characters a user will enter just only look at the first character entered. In fact, it is probably better since you will always have to handle possible user errors. If you want to you can issue an alert that they entered too many by getting the characters.count. You might want an alert if they don't answer at all. The code below will work as is if you set up a storyboard with 1 NSTextField and one button and connect them. If you have more than one textfield, i.e. like a multiple choice test, just set up all the text fields the same way.
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var firstLetter: NSTextField!
Add as many text fields as you need:
#IBOutlet weak var secondLetter: NSTextField!
#IBOutlet weak var thirdLetter: NSTextField!
etc.
#IBAction func button(sender: AnyObject) {
var firstEntry = firstLetter!.stringValue
var index1 = firstEntry.startIndex
if firstEntry.characters.count > 1 {
runMyAlert("Bad USER! ONLY ONE Character!")
}
if firstEntry == "" { //left it blank
runMyAlert("You need to enter at least one character!")
exit(0) //or you'll crash on next line
}
var nameLetter1:Character = firstEntry[index1]
print( "First Letter == \(nameLetter1) ")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func runMyAlert( alertMessage: String){
var myWindow = NSWindow.self
let alert = NSAlert()
alert.messageText = "ERROR ERROR ERROR"
alert.addButtonWithTitle("OK")
alert.informativeText = alertMessage
alert.runModal()
}
}
I want to connect a second button to a label #IBOutlet var ourScore: UILabel! .
The first button is connected as
#IBAction func buttonPressed(sender: AnyObject) {
ourScore.text = "\(++score)"
}
How is it possible that I can add another button to the label, so when you click on the second button it works together. The first one counts it up and the second one needs to reset it, back to 0.
Your button isn't linked to your label outlet, the code of your IBAction makes reference to it. You posted this method:
#IBAction func buttonPressed(sender: AnyObject)
{
ourScore.text = "\(++score)"
}
so create a new method:
#IBAction func resetButtonPressed(sender: AnyObject)
{
score = 0;
ourScore.text = "\(score)"
}
Link that second IBAction method to your second button. Done.
You create the button in the same exact way you created the first one but instead of ++ it'd be -- (or whatever you want it to be).
The label is available to anything inside of your class so you can manipulate it and read from it anywhere.
So I have a storyboard with 3 buttons I want to just create 1 action for all those 3 buttons and decide what to do based on their label/id...
Is there a way to get some kind of identifier for each button?
By the way they are images, so they don't have a title.
#IBAction func mainButton(sender: UIButton) {
println(sender)
}
You can set a tag in the storyboard for each of the buttons. Then you can identify them this way:
#IBAction func mainButton(sender: UIButton) {
println(sender.tag)
}
EDIT: For more readability you can define an enum with values that correspond to the selected tag. So if you set tags like 0, 1, 2 for your buttons, above your class declaration you can do something like this:
enum SelectedButtonTag: Int {
case First
case Second
case Third
}
And then instead of handling hardcoded values you will have:
#IBAction func mainButton(sender: UIButton) {
switch sender.tag {
case SelectedButtonTag.First.rawValue:
println("do something when first button is tapped")
case SelectedButtonTag.Second.rawValue:
println("do something when second button is tapped")
case SelectedButtonTag.Third.rawValue:
println("do something when third button is tapped")
default:
println("default")
}
}
If you want to create 3 buttons with single method then you can do this by following code...Try this
Swift 3
Example :-
override func viewDidLoad()
{
super.viewDidLoad()
Button1.tag=1
Button1.addTarget(self,action:#selector(buttonClicked),
for:.touchUpInside)
Button2.tag=2
Button2.addTarget(self,action:#selector(buttonClicked),
for:.touchUpInside)
Button3.tag=3
Button3.addTarget(self,action:#selector(buttonClicked),
for:.touchUpInside)
}
func buttonClicked(sender:UIButton)
{
switch sender.tag
{
case 1: print("1") //when Button1 is clicked...
break
case 2: print("2") //when Button2 is clicked...
break
case 3: print("3") //when Button3 is clicked...
break
default: print("Other...")
}
}
You can create an outlet for your buttons and then implement:
#IBAction func mainButton(sender: UIButton) {
switch sender {
case yourbuttonname:
// do something
case anotherbuttonname:
// do something else
default: println(sender)
}
}
Swift 4 - 5.1
#IBAction func buttonPressed(_ sender: UIButton) {
if sender.tag == 1 {
print("Button 1 is pressed")
}
}
You have to set tag value to what you need and access it with
sender.tag
Assuming you gave them all proper names as #IBOutlets:
#IBOutlet var weak buttonOne: UIButton!
#IBOutlet var weak buttonTwo: UIButton!
#IBOutlet var weak buttonThree: UIButton!
You can use the following to determine which is which
#IBAction func didPressButton(sender: AnyObject){
// no harm in doing some sort of checking on the sender
if(sender.isKindOfClass(UIButton)){
switch(sender){
case buttonOne:
//buttonOne action
break
case buttonTwo:
//buttonTwo action
break
case buttonThree:
//buttonThree action
break
default:
break
}
}
Swift 3 Code:
In xcode Please set tag for each button first to work following code.
#IBAction func threeButtonsAction(_ sender: UIButton) {
switch sender.tag {
case 1:
print("do something when first button is tapped")
break
case 2:
print("do something when second button is tapped")
break
case 3:
print("do something when third button is tapped")
break
default:
break
}
}
You can do like this, just you have to give tag to all the buttons and do like this:
#IBAction func mainButton(sender: AnyObject)
{
switch sender.tag {
case 1:
println("do something when first button is tapped")
case 2:
println("do something when second button is tapped")
case 3:
println("do something when third button is tapped")
default:
println("default")
}
}
Use the outlets instead, tags clutter the code and make the readability way worse. Think about the poor developer that reads the code next and sees if sender.tag = 381 { // do some magic }, it just won't make any sense.
My example:
class PhoneNumberCell: UITableViewCell {
#IBOutlet weak var callButton: UIButton!
#IBOutlet weak var messageButton: UIButton!
#IBAction func didSelectAction(_ sender: UIButton) {
if sender == callButton {
debugPrint("Call person")
} else if sender == messageButton {
debugPrint("Message person")
}
}
[...]
}
You could also do this in a nice switch as well, which would make it even better.
Tested on Swift 5.1
Swift 4
add tag on button
let button = UIButton()
button.tag = 10
click event
#IBAction func mainButton(sender: UIButton) {
switch sender.tag {
case 10:
print("10")
case 11:
print("11")
default:
print("yes")
}
}
In this case you can use NSObject extension Accessibility Element UIAccessibility.
I have used accessibilityLabel and accessibilityIdentifier both are success in call and condition checking.
First
You can set a Accessibility Label or Identifier in the storyboard for each of the buttons in Identity inspector. Accessibility should be enabled.
To check/Identify button by
#IBAction func selectionPicker(_ sender: UIButton){
if sender.accessibilityLabel == "childType"{ //Check by accessibilityLabel
print("Child Type")
}
if sender.accessibilityIdentifier == "roomType"{ //Check by accessibilityIdentifier
print("Room Type")
}
performSegue(withIdentifier: "selectionViewSegue", sender:sender)
}
On Swift 3.2 and 4.0 with Xcode 9.0
Given the case you labeled your buttons "1", "2", "3":
#IBAction func mainButton(sender: UIButton) {
switch sender.titleLabel?.text {
case "1":
print("do something when first button is tapped")
case "2":
print("do something when second button is tapped")
case "3":
print("do something when third button is tapped")
default:
() // empty statement or "do nothing"
}
}
Swift 5.5
I have utility methods in other classes that do not have an instance of my ViewController, so I don't compare the sent objects to what is defined in the ViewController's IBOutlets.
I don't use tags if I can use a plain language identifier on my UI objects. I'd rather have plain language identifiers than numbers to identify my objects because it is easier for me. Just another way of doing it.
If I need to use a utility method, I set it up with a sender parameter so I can send the button and then figure out which button was clicked based on the assigned identity of the button within Storyboard.
For example:
class Utility {
func doSomething(sender: Any?) {
guard let button = sender as? NSButton else {
print("Unable to set button from sender.")
return
}
guard case buttonID = button.identifier?.rawValue else {
print("Unable to get button identifier.")
return
}
switch buttonID {
case: "firstButton":
_ = buttonID // Perform firstButton action
case: "secondButton":
_ = buttonID // Perform secondButton action
case: "thirdButton":
_ = buttonID // Perform thirdButton action
default:
// shouldn't get here - error?
}
}
}
In my ViewController I have the following buttons set up as IBOutlets and their identity is the same in Storyboard.
#IBOutlet weak var firstButton: NSButton?
#IBOutlet weak var secondButton: NSButton?
#IBOutlet weak var thirdButton: NSButton?
Then I have my IBActions:
#IBAction func firstButtonClicked(sender: Any?) {
utility.doSomething(sender: sender)
}
#IBAction func secondButtonClicked(sender: Any?) {
utility.doSomething(sender: sender)
}
#IBAction func thirdButtonClicked(sender: Any?) {
utility.doSomething(sender: sender)
}
In my case what i did, just like the answers above i used the tag to identify the specific button, what i added is that i added a UIButton extension that adds an id so that i can set a string id
i had three buttons with tags 0, 1 and 2
Then created the extension
extension UIButton {
var id: String {
let tag = self.tag
switch tag {
case 0:
return "breakfast"
case 1:
return "lunch"
case 2:
return "dinner"
default:
return "None"
}
}
}
When accessing a button in an IBAction i would just call:
sender.id
Select your first button and give it tag 0, and select second button and give it tag 1 and so on, in action check the tag bit and perform you functionalities on the basis of tag bit.:
switch sender as! NSObject {
case self.buttoneOne:
println("do something when first button is tapped")
case self.buttoneTwo:
println("do something when second button is tapped")
default:
println("default")
}