Swift's documentation of Remove from an array is "Removes and returns the element at the specified position." When I try it I don't get any values. If I do it in two steps it works fine. What am I missing?
Main code:
func GetItemAlert(s: String){
// Create an alert
let alert = UIAlertController(
title: "New item",
message: s,
preferredStyle: .alert)
// Add a text field to the alert for the new item's title
//alert.addTextField(configurationHandler: nil)
// Add a "cancel" button to the alert. This one doesn't need a handler
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
// Add a "OK" button to the alert. The handler calls addNewItem()
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in
}))
// Present the alert to the user
self.present(alert, animated: true, completion: nil)
}
func pop()-> String{
let removeid = Int(arc4random_uniform(UInt32(TheList.count)))
let val1 = TheList[removeid]
let val2 = TheList.remove(at: removeid)
tableView.reloadData()
return ""
}
#IBAction func btnPop(_ sender: Any) {
GetItemAlert(s: pop())
}
Extention:
import Foundation
class HatTrick
{
var item: String
public init(item: String)
{
self.item = item
}
}
extension HatTrick
{
public class func preload() -> [HatTrick]
{
return [
HatTrick(item: "A"),
HatTrick(item: "B"),
HatTrick(item: "C"),
HatTrick(item: "D"),
HatTrick(item: "E")
]
}
}
You are correct in saying that remove "Removes and returns the element at the specified position."
var a = [0, 1, 2, 3] // Creates an array
let b = a.remove(at: 2) // we removed a value from a and stored it in b
print(b) // Prints 2
Maybe it doesn't show val2 in the debugger cause Xcode optimized them out. You can try to call a function with them and see whether its still optimized away.
Val1 is still referenced inside the Array until its removed in the next call, so retained? If you care you could file a bugreport.
Related
i declare the contact variable as an empty string array
var contact = [String] ()
then I made a query to output the results from CloudKit, when I accessed the controller once, var contact succeeded in adding an array
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Note", predicate: predicate)
database.perform(query, inZoneWith: nil) { (record, error) in
for record: CKRecord in record! {
let name = record.value(forKeyPath: "content") as! String
print ("There is a note \"\(name)\"")
self.contact.append(name)
}
}
self.contact.append (name)
print ("There is a note \" \ (name) \ "")
but when accessing for the second time, the var contact becomes empty again
print ("check all array append \ (contact)")
Succes append firstime access the controller
Failed append secondtime access the controller
i use variable contact in other function
func sendSOS() {
if canSendText() {
//compese message with google link
let googleLink = "https://www.google.com/maps/place/" + String(myLatitude!) + "+" + String(myLongtitude!)
let SMStext = "EMERGENCY!!, Tolong Bantu saya di lokasi Latitude: " + String(myLatitude!) + "\n Longtitude: " + String(myLongtitude!) + " " + googleLink
let messsageCompose = MFMessageComposeViewController()
messsageCompose.messageComposeDelegate = self
messsageCompose.recipients = contact;
messsageCompose.body = SMStext
present(messsageCompose, animated: true, completion: nil)
}else{
// create the alert
let alert = UIAlertController(title: "No SMS available.", message: "Please find a better location and try again!", preferredStyle: UIAlertController.Style.alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
return
}
}
It's difficult to determine what's going on because you're leaving out how the contact array is initialized relative to your CKQuery.
The database.perform line looks a little suspicious. I'm pretty sure that returns an array of CKRecords so you should have:
database.perform(query, inZoneWith: nil) { records, error in
//records is an optional array
if let records = records{
for record in records{
//You might have to parse record.values to get its key/value pairs
let name = record["content"] as? String ?? "No Name"
print("There is a note: \(name)")
//:::
self.contact.append(name)
}
}
}
As a side note, I recommend using CKQueryOperation for all queries (docs). It's a cleaner way to manage data flowing from CloudKit.
I have a tableview that displays a sourceArray with a Name and Category properties. I want to filter the Category property of the sourceArray based on the users input from a UIAlertController but getting the error "Instance method 'contains' requires that 'UITextField' conform to 'StringProtocol'". Any help is greatly appreciated.
var sourceArray = [(Name: String, Category: String, Picture1: UIImage, Picture2: UIImage, Picture3: UIImage, Description: String)]()
#IBAction func filterButton(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "Category", message: nil, preferredStyle: .alert)
let action = UIAlertAction(title: "Filter", style: .default) { (action) in
print(textField)
let filtered = sourceArray.filter({$0.Category.contains(textField)})
self.filteredArray = filteredArray.isEmpty ? sourceArray : filtered
self.tableview.reloadData()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Biceps, Chest, Shoulders, etc."
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
This issue I was having is that textField was not being recognized as a String. So I added a constant text as a String and assigned the textField.text to it like so.
#IBAction func filterButton(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "Category", message: nil, preferredStyle: .alert)
let action = UIAlertAction(title: "Filter", style: .default) { (action) in
let text: String = textField.text!
let filtered = self.sourceArray.filter({$0.Category.contains(text)})
self.filteredArray = self.filteredArray.isEmpty ? self.sourceArray : filtered
self.tableview.reloadData()
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Biceps, Chest, Shoulders, etc."
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
Currently have a UITableView where there is an edit and delete button. Right now I trying to figure out how you can delete an element inside a map/dictionary from the database. For example, i want to remove:
dailyIntake { .
1568695516 {
amount : 12 .
timestamp : 1568695516.837234
}
Here is an image of my database:
Firestore Image
.
Here is my code in Swift: {
#objc func handleDeleteTap() {
print("Delete Button Tapped!")
let deleteOption = UIAlertAction(title: "Delete", style: .destructive) { (action) in
do {
// handle delete logic in the backend and update tableview
collectionReference(to: .intake).document(userUID).updateData([
"dailyIntake" : FieldValue.arrayRemove()
])
} catch let err {
print("Failed to Sign Out with Error:", err)
CustomAlert.showAlert(on: self, style: .alert, title: "Deletion Error", message: err.localizedDescription)
}
}
let cancelOption = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
CustomAlert.showAlert(on: self, style: .alert, title: "Delete current log?", message: nil, actions: [deleteOption, cancelOption], completion: nil)
}
I appreciate your time! Please help! Haha
Try and update the field with a transaction
First read the dictionary field, delete the element in the dictionary then update the field.
Docs here should help: https://firebase.google.com/docs/firestore/manage-data/transactions
You can do it by passing the element to arrayRemove()
FieldValue.arrayRemove(element)
So I figured it out on how to delete a certain value inside the dailyIntake map in Firestore (Cloud-DataBase).
#objc func handleDeleteTap() {
print("Delete Button Tapped!")
let deleteOption = UIAlertAction(title: "Delete", style: .destructive) { (action) in
do {
// handle delete logic in the backend and update tableview
collectionReference(to: .intake).document(userUID).updateData([
"dailyIntake.1568695516" : FieldValue.arrayRemove()
])
} catch let err {
print("Failed to Sign Out with Error:", err)
CustomAlert.showAlert(on: self, style: .alert, title: "Deletion Error", message: err.localizedDescription)
}
}
let cancelOption = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
CustomAlert.showAlert(on: self, style: .alert, title: "Delete current log?", message: nil, actions: [deleteOption, cancelOption], completion: nil)
}
With the "." symbol next to the key, it will actually remove a certain value in the map.
I've got a string array called languages, and I want to create as many UIAlertActions in a UIAlertController as there are elements in the array. I don't know how large the array will be, as the user can add languages to the array using the add option from the same UIAlertController.
loadLanguages() successfully loads userDefaults data into the languages array if there's any existing languages that've either been saved as the 2 starter languages on first app load and/or added by the user on subsequent uses of the app.
The add language option works, and is stored in userDfeaults (self.saveLanguages), and appended to the languages array.
However I'm not sure about creating UIAlertAction options for each language that is in the languages array. I've tried looping through the array to generate each menu item, as the languages array has the answer as to how many UIAlertActions should be displayed, but nothing appears.
After extensive searches I haven't come across anything covering this, but I'm sure there's an elegant approach.
FYI: languageChoiceButton is declared as:
var languageChoiceButton = UIAlertAction()
#objc func languageMenu(){
loadLanguages()
let chooseLanguageController = UIAlertController(title: "Vocabulary Tutor", message: "Choose a Language", preferredStyle: .actionSheet)
let addLanguage = UIAlertAction(title: "Add Language", style: .default, handler: { (action) -> Void in
let ac = UIAlertController(title: "Add a language", message: nil, preferredStyle: .alert)
ac.addTextField { textField in
textField.placeholder = "New language"
}
let submitAction = UIAlertAction(title: "Add", style: .default) { [unowned self, ac] (action: UIAlertAction!) in
self.newLanguage = ac.textFields?[0].text ?? ""
print("newLanguage: \(self.newLanguage)")
self.languages.append(self.newLanguage)
self.saveLanguages()
self.loadLanguages()
}
ac.addAction(submitAction)
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
self.present(ac, animated: true)
})
chooseLanguageController.addAction(addLanguage)
for language in languages {
languageChoiceButton = UIAlertAction(title: language.capitalized, style: .default, handler: { (action) -> Void in
self.chosenLanguage = language
self.title = self.chosenLanguage.capitalized
print("Chosen language is: \(self.chosenLanguage)")
self.loadInitialValues()
chooseLanguageController.addAction(self.languageChoiceButton)
})
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel) {
(action:UIAlertAction!) in
print("Cancel button tapped")
}
chooseLanguageController.addAction(cancel)
self.navigationController!.present(chooseLanguageController, animated: true, completion: nil)
}
Try to make an array while using certain languages and make a loop afterwards to add each of them to the alerts.
#objc func languageMenu(){
loadLanguages()
let chooseLanguageController = UIAlertController(title: "Vocabulary Tutor", message: "Choose a Language", preferredStyle: .actionSheet)
let i = languages.count - 1
for n in 0...i{
chooseLanguageController.addAction(UIAlertAction(title: arrayLanguage[n].language, style: .default, handler: { (action) in
print(self. languages[n])
}))
}
self.present(chooseLanguageController, animated: true, completion: nil)
}
I need to make a function that takes in an array of buttons and an array of closures. In the end, I am aiming to attach to each button an action from the array, and set up each button in my notification. Here's my function:
func makeButtons(buttons: [UIButton], withActions actions: [() -> Void]){
var bottomAnchor: NSLayoutYAxisAnchor?
bottomAnchor = notificationView.bottomAnchor
buttons.forEach({ (button) in
button.actionHandle(controlEvent: .touchUpInside, action: actions[buttons.index(of: button)!])
notificationView.addSubview(button)
button.bottomAnchor.constraint(equalTo: bottomAnchor!, constant: -20).isActive = true
button.rightAnchor.constraint(equalTo: notificationView.rightAnchor, constant: -20).isActive = true
button.leftAnchor.constraint(equalTo: notificationView.leftAnchor, constant: 20).isActive = true
button.heightAnchor.constraint(equalToConstant: 40).isActive = true
bottomAnchor = button.topAnchor
})
}
I am indeed using an extension to be able to add a closure for UIButton target using actionHandle(). I got it off Stack. Here's that:
extension UIButton {
private func actionHandleBlock(action:(() -> Void)? = nil) {
struct __ {
static var action :(() -> Void)?
}
if action != nil {
__.action = action
} else {
__.action?()
}
}
#objc private func triggerActionHandleBlock() {
self.actionHandleBlock()
}
func actionHandle(controlEvent control: UIControlEvents, action:#escaping () -> Void) {
self.actionHandleBlock(action: action)
self.addTarget(self, action: #selector(triggerActionHandleBlock), for: control)
}
}
Also, here's how I'm using the make Buttons function:
func setButtonsWithAction(){
let button1 = UIButton()
let button2 = UIButton()
addButtons(buttons: [button1, button2], withActions: [self.sayHi, self.sayGoodbye])
}
func sayHi(){
print("Hi there")
}
func sayGoodbye(){
print("Goodbye")
}
All sets up ok, just as I want. However, the problem I am facing is that regardless on which button I click, it performs the last function from my closure array. So in this case it prints "Goodbye" regardless of which button I click.
What I need is:
click button1 -> print Hi
click button2 -> print Goodbye
What is the problem, I can't seem to figure this out.
This should definitely show you the way.
Let me know how it goes.
fileprivate func toolbarButton(title: String, closure: Selector ) -> UIButton {
let button = UIButton(type: .system)
button.setTitle(title, for: .normal)
button.tintColor = .darkGray
button.addTarget(self, action: closure, for: .touchUpInside)
return button
}
use like this:
lazy var nearButton = toolbarButton(title: "Near Me", closure: #selector(handleToolButtonNear))