Swift UIAlertController fire once per row select textfield edited - swift

I have a UIPickerView with an if statement in selectedRowInComponent that checks the value of a textField. If the user inputed number in the textfield is greater than 10, an alert is called. All is working well except I'd like to have the alert only fire a single time after the row is selected and the field is edited. As it stands, the alert is called every time the field is updated/edited with a value greater than 10. The initial alert is sufficient. Any tips on how I can accomplish this? Thanks!
if (inputField.text! as NSString).doubleValue > 10 {
SweetAlert().showAlert("Number is greater than 10", subTitle: "Please select number less than 10", style: AlertStyle.CustomImag(imageFile: "alertimage.png"))
}

Quick and Dirty:
Create a bool variable publicly available in your viewController and initialize it with false (lets call the variable 'alertAlreadyShown')
Then just make an if statement about that bool in your pickerView method
if (inputField.text! as NSString).doubleValue > 10 {
if (alertAlreadyShown == false) {
SweetAlert().showAlert(...)
alertAlreadyShown = true
}
}
But don't forget to reset the bool at appropriate times to enable the alert again

There's two ways you could go about this. One would be (and the most user friendly in my opinion) only show values of less than ten in your picker view. The other option would be to set a bool for whether or not the alert had been displayed, and add that as a check to your if statement.

Related

How to force the order of UIKit pop up menu button items?

I have a couple of UIKit pop-up menu buttons with identical menu items on the same screen in a Swift app. The buttons are built by calling a function that uses an array of strings to create the list of menu items.
The problem is that depending on the button's vertical position on the screen, the menu items may appear in the order specified by the function, or reversed. If the button is in the upper half of the screen, the menu items are listed in the correct order. If the button is in the lower half of the screen the menu items are listed in reverse order.
I would prefer the menu items to appear in the same order regardless of the button's position on the screen. I could check the button location and have the menu creation function reverse the order, but that seems kind of clunky. I am hoping there's a cleaner way to override this behaviour.
The code and array used to create the button menus:
let buttonMenuItems = ["Spring","Summer","Autumn","Winter"]
func createAttributeMenu(menuNumber: Int)->UIMenu {
var menuActions: [UIAction] = []
for attribute in buttonMenuItems {
let item = UIAction(title: attribute) { action in
self.updateMenu(menuID: menuNumber, selected: attribute)
}
menuActions.append(item)
}
return UIMenu(title: "", children: menuActions)
}
The result is this:
Versions I'm using now in testing: Xcode 14.1, iOS 16.1, but I have seen this behaviour on earlier versions as well. (back to iOS 14.x)
Starting with iOS 16, there is a .preferredMenuElementOrder property that can be set on the button:
case automatic
A constant that allows the system to choose an ordering strategy according to the current context.
case priority
A constant that displays menu elements according to their priority.
case fixed
A constant that displays menu elements in a fixed order.
Best I can tell (as with many Apple definitions), there is no difference between .automatic and .priority.
From the .priority docs page:
Discussion
This ordering strategy displays the first menu element in the UIMenu closest to the location of the user interaction.
So, we get "reversed" order based on the position of the menu relative to the button.
To keep your defined order:
buttonNearTop.menu = createAttributeMenu(menuNumber: 1)
buttonNearBottom.menu = createAttributeMenu(menuNumber: 2)
if #available(iOS 16.0, *) {
buttonNearBottom.preferredMenuElementOrder = .fixed
buttonNearTop.preferredMenuElementOrder = .fixed
} else {
// out of luck... you get Apple's "priority" ordering
}

Mutually exclusive radio buttons for MacOS in Swift

I've got five radio buttons, and selecting one should deselect the others.
I've been over a lot of the questions here about radio buttons in Swift, but they're either for iOS or outdated versions of Swift, because Xcode isn't offering me options like ".isSelected". I've got ".isEnabled" but clearly semantics matter here, because "enabled" isn't the same thing as "selected" and it shows.
Writing my code as a series of "if-else" statements along these lines:
func disableUnselectedButtons() {
if Button2.isEnabled == true {
Button1.isEnabled = false
Button3.isEnabled = false
Button4.isEnabled = false
Button5.isEnabled = false
}
}
results in a situation where I can select all five buttons, and can't DEselect any of them after another has been selected. I've tried variations of .on/.off as well, and can't find the right one for this situation.
It's also clumsy as heck to write a method with five if-else statements along those lines. So there's that.
What's the best way to go about implementing this?
If your radio buttons have the same superview and have the same action then they should work as expected.
To set the same action for each of your radio buttons you can do one of the following.
If you are using Storyboards, open both storyboard and related NSViewController swift file. Ctrl-drag your first radio button to the swift file. Then do the same for each of the other radio buttons ensuring you are dragging onto the function generated from the first Ctrl-drag.
If you are creating the radio buttons in code then set the action parameter in the init for each radio button to be the same.
Another way to approach this is to represent the buttons as a Set and then it's easy to iterate through them and configure their state. The below code actually allows for allowing multiple selections to support a scenario that wants to "select three of the six options".
let allButtons = Set(1...5). //or however many you want
func selectActiveButtons(_ activeButtons: Set<Int>, from allButtons: Set<Int>){
let inactive = allButtons.subtracting(activeButtons)
setButtonState(forButtons: inactive, isSelected: false)
setButtonState(forButtons: activeButtons, isSelected: true)
}
func setButtonState(forButtons buttons: Set<Int>, isSelected: Bool) {
buttons.forEach{
//remove below line and replace with code to update buttons in UI
print("Button \($0): \(isSelected ? "Selected" : "Unselected")")
}
}
// select buttons 1 & 3.
//If wanting a "classic" radio button group just use one value in the arrayLiteral.
selectActiveButtons(Set(arrayLiteral: 1,3), from: allButtons)

Use a text field to set the title of an MKAnnotation in Swift

having registered a long press at a point on the map, I'd like a text field to pop up, so I can set the input as the title of a pin dropped at that point. How would I go about doing this?
Heres my code which currently registers a longpress and drops a pin, that all works fine! I'm not sure how to bring up a text field and get the users input though
func DropPin(gestureRecognizer:UIGestureRecognizer) {
if gestureRecognizer.state == .Began {
var point:CGPoint = gestureRecognizer.locationInView(self.Map)
var pinLoc: CLLocationCoordinate2D = self.Map.convertPoint(point, toCoordinateFromView: self.Map)
let x = CustomAnnotation(coordinate: pinLoc, title: "Pin", subtitle: "Pin", imageName: "TouchPin")
self.Map.addAnnotation(x)
}
}
There are several ways to do so, but you can choose one of the two for example.
In your view, you add a text field where the user set the title before you drop the pin. But that might not be intuitive or look bad (if you need to have a full screen map for example).
So, what I would suggest is to fire an UIAlertController with a text field inside. Here is an example of how you could do it.
Either you :
Create the annotation
Add it to the map
An alert appear
The user enter a name (keep it somewhere)
You retrieve the last annotation you've added
You set it's title with the name entered previously
Or :
You detect your long press
An alert appear
The user enter a name (keep it somewhere)
Create your annotation with the name entered previously
Add it to the map
Those are just 2-3 examples. You might think of something else :)

How do I control the UIPageViewController itemIndex in Swift?

I have set up a UIPageViewController. I therefore have two UIViewControllers and one UIPageViewController.
I am using this code and I would like to decrease or increase a variable called "character" when the user swipes forward or back.
class PageItemController: UIViewController {
/* As we’ll have only one class for all content controllers, we need to somehow identify different content items. In order to do it, each PageItemController should have its index and image name.*/
var itemIndex: Int = 0 {
didSet {
if itemIndex == 0 {
character = 1
}
if itemIndex == 1 {
character = 2
}
}
}
...
}
What I am doing is:
Tap on the Check button to check the value of the variable "character"
"Character 1" is being displayed
Swipe to the right
Tap on the Check button to check the value of the variable "character" once again
"Character 2" is being displayed
Swipe to the left
Tap on Check button
"Character 2" is being displayed. But wait, that variable character should contain "Character 1" instead!
So why is it happening? It should display character 1 instead of character 2.
If you need more details, check out this video: https://www.dropbox.com/s/gd6c7p0xvclixcr/IMG_0886.mov?dl=0
Check this answer:
https://stackoverflow.com/a/27934069/1135714
You need to workaround your way in by setting up multiple variables and edit their values when some UIPageControllers methods are activated.

Strange issues with tag label 0?

I have created a bar over the keyboard for textfields with previous/next/done button selections. In doing so, I noticed an odd occurance with my tags that I used to navigate between the textfields. I am creating my interface programmatically with a loop, and as such, just set the tag values to the loop variable i.
I started the i variable at 0 so the very first text field created had a tag of zero. Basically what was happening is the 'previous' button functionality would only go so low as 1. It wouldn't even go back to the text field with the 0 tag. The only way to fix this was to increase all tag values by 1 so the first text field started at 1 instead of zero.
Here is my code. Is there a bug in my code that I cannot see? or is this a weird issue with tags?
-(void)gotoPrevTextfield{
// If the active textfield is the first one, can't go to any previous
// field so just return.
UITextField *textField = (UITextField *)[inputsView viewWithTag:0];
NSLog(#"%i",textField.tag);
NSLog(#"%i",txtActiveField.tag);
if (txtActiveField == textField) {
NSLog(#"returning at previous");
return;
}
else {
NSLog(#"set responder");
// Otherwise if a different textfield has the focus, the operation
// of "previous" button can be done and set the previous as the first
// responder.
textField = (UITextField *)[inputsView viewWithTag:txtActiveField.tag - 1];
NSLog(#"%i",textField.tag);
NSLog(#"%i",txtActiveField.tag);
[textField becomeFirstResponder];
}
}
Note that unset tags default to 0 so that is almost a poor choice. You may be getting another view that you don't expect.
A fairly good practice is to add some constant such as 100, consider making the constant a const int or #define for clarity.