So essentially I'm creating a calculator type app, in which I only want to intake numbers, decimals, and the negative sign. I'm fairly new to Xcode and swift so I wouldn't consider my knowledge extensive when it comes to knowing how to handle these sorts of issues.
Here is my current attempt at a solution
let v = Double(inputOne.text!)!
(inputOne. is the associated text field)
This works well in my algorithm when everything is inputted as it should be, however when inputting anything other than a valid double I receive "Fatal error: Unexpectedly found nil while unwrapping an Optional value", and the app crashes.
I figured someone may have already come up with a creative solution to this problem or that there may be some command in Xcode that limits text fields in this way, however I am new to Xcode and unaware of such things if they exist. Maybe there is some way I can implement an error message that occurs whenever something other than a valid double is inputted without crashing the app? Any creative solutions would be appreciated.
You can use a UITextView as your input field (if you want a one-line input, use a textField). Then, simply set your ViewController as the textView's delegate -inside your ViewController add the following line (and don't forget to inherit UITextViewDelegate):
textView.delegate = self
Essentially, what this does is allow the ViewController to take ownership of some of the textView's functionalities. In this case, we are interested in filtering the user input and for that you can use the following function:
/// Intercept whatever character the user is typing on the keyboard
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// If the user presses "enter", handle it the way you want
guard text.rangeOfCharacter(from: CharacterSet.newlines) == nil else {
// Handle new line
}
// If character is a digit, a dot, or a dash, allow input, else reject it.
if text == "-" || text.rangeOfCharacter(from: CharacterSet.decimalDigits) != nil || text == "." {
return true
} else {
return false
}
}
Related
I have multiple text fields in my app and want all of them to not crash if the textfield is empty.
I have the code that fixes this but I am wondering if there is something i can write that shortcuts this code?
SO i could write
fixNilError()
and it would run the code:
if textField.text != nil {
print("Success")
}
textField.text returns an optional value, which means that the value is either something, or nil.
To handle this value, you need to "unwrap" it, and one of the cleaner way to make it would be this one:
if let fieldText = textField.text {
print(fieldText)
}
Not sure it helps you to make your code shorter, but optionals is a clever way to make it secure.
use method hasText() to check multiple textfield empty or not see below code and code accordingly.
if !text1 .hasText(){ // in this case text1 does not contain text
// deliver appropriate message to user here
return;
}else if !text2 .hasText(){
// deliver appropriate message to user here
return;
}
Heys guys,
I am pretty new into programming and therefore I've followed I course on Udemy to teach me Swift 2.2.
For learning purpose I have been trying to program a BMI-calculator where I have a textfield (which just displays the value) and a slider where I can put my weight in kg. After dragging the slider the value is visible in the textfield. I cannot put a value into the textfield so that it is displayed on the slider!
The same textfield-slider relation is used with the height in cm. Now I created an IBAction the bring my kgSlider.value into my kgField.text and it looks like this:
#IBAction func kgSet(sender: AnyObject) {
kgField.text! = String(Int(kgSlider.value))
}
Thats works fine but I unwrapped (like the teacher in the course) without knowing, if there really is a value. Okay, I know in this case that there will be a value, but I would like to go more real and therefore I tried to use an Optional-Binding to find out, if there is a value instead of directly unwrap it.
Therefore I used the cm.Field and the cm.Slider in my code but it doesn't work for now and I don't know why. The code is the following:
#IBAction func cmSet(sender: AnyObject) {
if let tempCm = String(Int(cmSlider.value)) as String! {
cmField.text = tempCm
}
}
So I created the constant called tempCM which will got the value from the cmSlider, if there is a value. Therefore I casted the cmSlider.value like in the other IBAction into an Int and then into a String. If there is the value it will carry it into the cmField.text. This didn't work, therefore I tried to use the "as String!" statement but know I get always 0 instead of the correct value.
So what am I doing wrong there?
So, this should compile fine and provide you with your desired result.
#IBAction func cmSet(sender: AnyObject) {
if let tempCm = String(Int(cmSlider.value)) {
cmField.text = tempCm
}
}
You could also try this
cmField.text = String(Int(cmSlider.value)) ?? " "
in the second example, you are using the optional operator to say if you can convert this to an Int then that Int to a string set the cmField.text property to its value, otherwise use a blank space as the default value.
Can someone please help me with my problem, the problem is that I want my first initial value that I enter into the textfield to be only number from 1 to 9, I am using Decimal pad so I also don't want my initial value to be a decimal point,but after the first value(only number from 1 t0 9) users are allowed to enter a only one decimal.I have done only one decimal thingy but cannot figure out how to let users not enter zero or decimal point as their initial/Starting value. please help me, Thank you in advance. I asked this before many said it is available online, but I cannot find it.If you upload the code it would mean the world to me.
If I understand correctly, and you want to prohibit the user from entering a "." or "0" as the first character in the textField, then you need to set the textField's delegate to an instance of a class which contains this method:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if textField.text == "" && (replacementString == "0" || replacementString == ".") {
return false
}
return true
}
I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.
I will soon be working on an application which needs to get the currently selected text in the frontmost application window, be it Safari, Pages, TextEdit, Word, etc., and do something with that text.
My goal is to find a solution that works with as much applications as possible. So far I thought about using AppleScript, but that would limit the amount of applications which could be used with my service. At least these common applications must be supported: Safari, Firefox (no AppleScript?), Word, Pages, Excel, TextEdit, ...
I also thought about keeping the clipboard's content in a temporary variable then simulating a text copy operation (Cmd-C), getting the text and then put the original content back in. This would probably highlight the Edit menu item when the copy operation is simulated and seems a bit hacky to me. IMO this solution doesn't seem good enough for a commercial product.
I am also looking to get more than the selection (i.e: the complete contents of the page in Safari or Word, etc.) to add some additional features in the future.
Any ideas/details on how to implement this behavior?
Thanks in advance for any hints!
N.B: I need to support at least 10.4 and up, but ideally older than 10.4 too.
UPDATE:
The solution I've opted for: Using the "Chain of Responsibility" design pattern (GOF) to combine 3 different input methods (Pasteboard, AppleScript and Accessibility), using the best available input source automatically.
Note that when using NSAppleScript's executeAndReturnError: method which returns an NSAppleEventDescriptor (let's say a "descriptor" instance), for the [descriptor stringValue] method to return something, in your AppleScript you must use "return someString" OUTSIDE of a "tell" block else nothing will be returned.
Here's the Swift 5.5 implementation of what is described in the accepted answer.
extension AXUIElement {
static var focusedElement: AXUIElement? {
systemWide.element(for: kAXFocusedUIElementAttribute)
}
var selectedText: String? {
rawValue(for: kAXSelectedTextAttribute) as? String
}
private static var systemWide = AXUIElementCreateSystemWide()
private func element(for attribute: String) -> AXUIElement? {
guard let rawValue = rawValue(for: attribute), CFGetTypeID(rawValue) == AXUIElementGetTypeID() else { return nil }
return (rawValue as! AXUIElement)
}
private func rawValue(for attribute: String) -> AnyObject? {
var rawValue: AnyObject?
let error = AXUIElementCopyAttributeValue(self, attribute as CFString, &rawValue)
return error == .success ? rawValue : nil
}
}
Now, wherever you need to get the selected text from the frontmost application, you can just use AXUIElement.focusedElement?.selectedText.
As mentioned in the answer, this is not 100% reliable. So we're also implementing the other answer which simulates Command + C and copies from the clipboard. Also, ensure to remove the new item from the Clipboard if not required.
If you don't need selected text very frequently, you can programmatically press Command+C, then get the selected text from clipboard. But during my test, this is only works if you turn off App Sandbox (can't submit to Mac App Store).
Here is the Swift 3 code:
func performGlobalCopyShortcut() {
func keyEvents(forPressAndReleaseVirtualKey virtualKey: Int) -> [CGEvent] {
let eventSource = CGEventSource(stateID: .hidSystemState)
return [
CGEvent(keyboardEventSource: eventSource, virtualKey: CGKeyCode(virtualKey), keyDown: true)!,
CGEvent(keyboardEventSource: eventSource, virtualKey: CGKeyCode(virtualKey), keyDown: false)!,
]
}
let tapLocation = CGEventTapLocation.cghidEventTap
let events = keyEvents(forPressAndReleaseVirtualKey: kVK_ANSI_C)
events.forEach {
$0.flags = .maskCommand
$0.post(tap: tapLocation)
}
}
performGlobalCopyShortcut()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { // wait 0.05s for copy.
let clipboardText = NSPasteboard.general().readObjects(forClasses: [NSString.self], options: nil)?.first as? String ?? ""
print(clipboardText)
}
Accessibility will work, but only if access for assistive devices is on.
You'll need to get the current application, then get its focused UI element, then get its selected text ranges and its value (whole text) and selected text ranges. You could just get its selected text, but that would either concatenate or ignore multiple selections.
Be prepared for any of those steps to fail: The app may not have any windows up, there may be no UI element with focus, the focused UI element may have no text, and the focused UI element may have only an empty selected text range.