I am a newcomer in xcode and swift, and I am having a problem with using two IBActions to allow a button to be enabled. I have 2 text fields, and I have a button that is disabled. I want the button to be enabled when both of the text fields are filled in. How can I do this? So far I have declared two functions with IBActions for the two text fields:
#IBAction func yourWeightEditingDidBegin(sender: AnyObject) {
}
#IBAction func calorieNumberEditingDidBegin(sender: AnyObject) {
}
Thanks!
One way is to use the UITextFieldDelegate function instead of IBOutlets:
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
if yourWeight.text != "" && calorieNumber.text != "" {
button.enabled = true
}
return true
}
I, too, implement UITextFieldDelegate, but I use shouldChangeCharactersInRange. This way, the status of that button changes as the user types:
If dealing with only two text fields, it looks like:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// get the value this text field will have after the string is replaced
let value: NSString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
// get the value of the other text field
let otherValue = textField == yourWeight ? calorieNumber.text : yourWeight.text
// only enable done if these are both non-zero length
doneButton.enabled = (value != "" && otherValue != "")
return true
}
Clearly, for this to work, you must specify the delegate for both of those text fields (either in IB or programmatically). I also generally marry the above with the "auto-enable return key" option for the text fields, too.
You don't. Instead, try something like this
var weightIsFilled = false
var calorieNumberIsFilled = false
#IBAction func yourWeightEditingDidBegin(sender: AnyObject) {
if valueOfWeightTextFieldIsValid() {
self.weightIsFilled = true
}
if self.weightIsFilled && self.calorieNumberIsFilled {
self.enableButton()
}
}
#IBAction func calorieNumberEditingDidBegin(sender: AnyObject) {
if valueOfCalorieNumberTextFieldIsValid() {
self.calorieNumberIsFilled = true
}
if self.weightIsFilled && self.calorieNumberIsFilled {
self.enableButton()
}
}
You also may want to be using IBAction functions that are called when the textfield's value changes, not when editing begins on it.
Related
Im using the search bar to filter data from the database and will populate the tableView. Im getting an error on the isSearching part.
Value of type 'DataSnapshot' has no member 'contains'
Heres the code.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
isSearching = false
view.endEditing(true)
tableView.reloadData()
} else {
isSearching = true
filteredColorRequests = colors.filter{$0.contains(searchBar.text!)}
tableView.reloadData()
}
}
How
You certainly want to search for a specific String property for example a name.
And rather than getting the search string form the bar use the searchText parameter which is already non-optional.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
isSearching = false
view.endEditing(true)
} else {
isSearching = true
filteredColorRequests = colors.filter{(($0.value as! [String:Any])["name"] as! String).contains(searchText)}
}
tableView.reloadData()
}
The code i have updates the label when i change the TextField content, but it's not what I need:
property aTextField : missing value
property aTextLabel : missing value
on controlTextDidChange_(aNotification)
log aTextField's stringValue
aTextLabel's setStringValue: aTextField's stringValue
end textDidChange_
I need something like:
property aTextLabel : missing value
on applicationWillFinishLaunching_(aNotification)
repeat
set MyFileTextContent to (do shell script "cat /Users/Johann/Desktop/myLabelValue.txt")
aTextLabel's setStringValue:MyFileTextContent
end repeat
end applicationWillFinishLaunching_
But obviously this does not work, but I do not know how to achieve my goal.
Warning: I don't write AppleScripts. But this will work for simple cases. Note that most (all ?) implementations of tail -f use a delay, then stat the file for changes and then if there are changes it reads the file. So keep a variable to show the old contents and test against that.
on applicationWillFinishLaunching_(aNotification)
set oldContents to ""
repeat
set MyFileTextContent to (do shell script "tail /Users/Johann/Desktop/myLabelValue.txt")
set aTextLabel to MyFileTextContent
if MyFileTextContent is not equal to oldContents
set oldContents to MyFileTextContent
set aTextField to MyFileTextContent
end if
delay 5
end repeat
end applicationWillFinishLaunching
You will need to adjust the setting of aTextLabel. You will need to experiment with the delay. Finally, note that I used a tail rather than cat. Adjust according to your needs._
I can only give you an example using swift. Hopefully this will get you on track. This is a simpler approach that uses Timer.scheduledTimer. This is the complete code for a ViewController. The storybook should have a label (NSTextField) called fileContents. There is a start and end button. Create a text file called mytext.txt on your desktop for testing. If you are having problems getting it to work, I could post the entire Xcode project on github.
//
// ViewController.swift
// monitorFile
//
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var fileContents: NSTextField!
var monitorTimer: Timer!
var homeDir : URL!
var desktopFile : URL!
// This keeps us from updating the display when content didn't change
var oldContent: String!
override func viewDidLoad() {
super.viewDidLoad()
homeDir = FileManager.default.homeDirectoryForCurrentUser
desktopFile = homeDir.appendingPathComponent("Desktop", isDirectory: true).appendingPathComponent("mytext").appendingPathExtension("txt")
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
override func viewWillDisappear() {
if (monitorTimer != nil) {
endTimer()
}
}
func startMonitor() {
if (monitorTimer == nil) {
// timeInterval is a double representing seconds
monitorTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateDisplay), userInfo: nil, repeats: true)
}
}
#objc func updateDisplay() {
let newContent = readIt(location: desktopFile)
if (newContent != oldContent) {
oldContent = newContent
fileContents.stringValue = newContent
}
}
func endTimer() {
if (monitorTimer != nil) {
monitorTimer.invalidate()
}
}
func readIt(location: URL) -> String {
var contents = "No content"
do {
contents = try String(contentsOf: location)
} catch {
print("Failed reading from URL: \(desktopFile), Error: " + error.localizedDescription)
}
return contents
}
#IBAction func stopMonitor(_ sender: NSButton) {
endTimer()
}
#IBAction func startMonitor(_ sender: NSButton) {
startMonitor()
}
}
The solution:
property NSTimer : class "NSTimer"
property myTextField : missing value
on log1:sender
performSelector_withObject_afterDelay_("log1:", missing value, 1.0)
set myText to (do shell script "cat texFile.txt")
myTextField's setStringValue:myText
end log1:
on applicationWillFinishLaunching:aNotification
performSelector_withObject_afterDelay_("log1:", me, 0)
end applicationWillFinishLaunching:
I'm making an app where students enter their grade in a specific text field. I want them to enter a number from 0 to 100. I was just wondering how do we set a maximum NUMBER value for a textfield.
I'm assuming I'm supposed to make some type of action but I'm not sure what action type to use or the code to set a maximum value.
Any help would be very appreciated :) !!
You can use a UIPickerView if you want to present a limited number of fixed answers: http://sourcefreeze.com/ios-uipickerview-example-using-swift/. But if you want to have them enter it in a textbox, you can validate the input in the textFieldDidEndEditing method like so:
func textFieldDidEndEditing(textField: UITextField) {
if textField.text.toInt() < 0 || textfield.text.toInt() > 100 {
//Here you can present an alert, change the input or clear the field.
}
}
An alternative is to make the score 0 when negative and 100 when the user enters something more than 100. This allows you to enter fractional score like 72.5:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.textField.delegate = self
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
// Make sure the user entered a number
guard let score = Double(textField.text!) else {
let alert = UIAlertController(title: "Invalid Score", message: "Score must be a number from 0 to 100", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
return false
}
if score < 0 {
textField.text = "0"
} else if score > 100 {
textField.text = "100"
}
return true
}
}
internal func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if ((self.inputTextField?.text)!.characters.count > 10 && range.length == 0)
{
return false
//return false // return NO to not change text
}else{
return true
}
}
Note: Import text field UITextFieldDelegat.
I'm working on a text editor and am having trouble with string optionals. I want to use a textView's string method; since it's an optional, Xcode insists that I unwrap it. When I use forced unwrapping (which is what Xcode recommends) I get runtime errors; I'd prefer to use optional chaining so that nil values don't cause a crash. But I can't get optional chaining to work.
To get open and save working, I'm trying to use self.textViewOne.string = self.text in windowControllerDidLoadNib and self.text = self.textViewOne.string in dataOfType. But I get crashes of "unexpectedly found nil while unwrapping an Optional value". Documentation tells me I should use if-let or even if-var to do this properly, but I can't; when I try to add if-let or if-var, I get an "Expected pattern" error, probably because the self.text variable already exists - but I don't know how else to unwrap properly.
In dataOfType I even tried to unwrap it with a kludgey regular if-then statement:
if ((self.textViewOne.string) != nil)
{
self.text = self.textViewOne.string
}
else
{
self.text = ""
}
but even that doesn't work: Xcode still insists on a ! after self.textViewOne.string, and with or without the ! I still get a "fatal error: unexpectedly found nil while unwrapping an Optional value".
EDIT: Here's the complete code for the Document class as it currently stands (including a bit of tinkering after the original post, but still getting the error):
import Cocoa
class Document: NSDocument {
#IBOutlet var textViewOne: NSTextView!
#IBOutlet var textViewTwo: NSTextView!
var text = ""
override init() {
super.init()
// Add your subclass-specific initialization here.
}
override func windowControllerDidLoadNib(aController: NSWindowController) {
// The window has loaded, and is ready to display.
// Take the text that we loaded earlier and display it in the text field
super.windowControllerDidLoadNib(aController)
self.textViewOne.string = self.text
}
override class func autosavesInPlace() -> Bool {
return true
}
override var windowNibName: String? {
// Returns the nib file name of the document
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this property and override -makeWindowControllers instead.
return "Document"
}
override func dataOfType(typeName: String?, error outError: NSErrorPointer) -> NSData? {
// Convert the contents of the text field into data, and return it
if (self.textViewOne == nil)
{
println ("self.textViewOne is nil.")
}
if let someText = self.textViewOne.string {
self.text = someText
} else {
self.text = ""
}
return self.text.dataUsingEncoding( NSUTF8StringEncoding, allowLossyConversion: false)
}
override func readFromData(data: NSData, ofType typeName: String?, error outError: NSErrorPointer) -> Bool {
// Attempt to load a string from the data; if it works, store it in self.text
if data.length > 0
{
let string = NSString( data: data, encoding: NSUTF8StringEncoding)
self.text = string!
}
else
{ self.text = "" }
return true
}
}
What about using an if let to unwrap a non-nil value from self.textViewOne?
if let someText = self.textViewOne.string {
self.text = someText
} else {
self.text = ""
}
I have a UITextView which I am using as text entry in a translation application. When the user presses return/search then I want to do some action e.g. searching for word from textView. I want to do some action like we do with IBAction.
Set up delegate of the textView. Than add shouldChangeTextInRange for detecting of the return/search buttons and performAction for you're custom action.
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
textView.resignFirstResponder()
performAction()
}
return true
}
func performAction() {
}