In my collection view cell, I have a button which successfully saves the current user (PFUser) to an array of users of each event (which are the objects at index path).
However, I would like to add an alert view, but have been unsuccessful in putting the enormous chunk of code inside the alert action block. I suppose I could create another method but I feel there's a simpler way to do it.
When I tried putting it in, A. the sheer number of brackets threw me off a bit and B. it didn't recognize "alertController" anymore at the bottom of the method.
Thanks for the help like always.
func buttonTapped(sender: AnyObject) {
let button = sender as! UIButton
let view = button.superview
let cell = view?.superview as! EventCell
let indexPath = collectionView.indexPathForCell(cell)
// print(indexPath)
if let indexPath = indexPath {
if let event = events?[indexPath.row] {
//pops alert vc
var alertController : UIAlertController = UIAlertController(title: event.eventTitle, message: nil, preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
let saveUserToEvent = UIAlertAction(title: "Yes", style: .Default) { _ in
// WHAT TO DO HERE? I want to add the stuff below
}
alertController.addAction(saveUserToEvent)
var attendees = [String]()
if let attendeesTmp = event["attendees"] as?[String] {
attendees = attendeesTmp;
}
if let objId = PFUser.currentUser()?.objectId {
var found = false
for objIdd in attendees {
if objIdd == objId {
found = true
break;
}
}
if !found {
attendees.append(objId)
event["attendees"] = attendees;
event.saveInBackground()
}
}
if let user = PFUser.currentUser() {
var eventsAttending = [String]()
if let eventsAttendingTmp = user["eventsToAttend"] as?[String] {
eventsAttending = eventsAttendingTmp;
}
if let eventId = event.objectId {
var found = false
for eventIdd in eventsAttending {
if eventIdd == eventId {
found = true
break;
}
}
if !found {
eventsAttending.append(eventId)
user["eventsToAttend"] = eventsAttending;
user.saveInBackground()
}
}
}
}
}
}
Did you put
alertController.addAction(saveUserToEvent)
In the brackets as well? It stays outside.
This should work
func buttonTapped(sender: AnyObject) {
let button = sender as! UIButton
let view = button.superview
let cell = view?.superview as! UITableViewCell
let indexPath = collectionView.indexPathForCell(cell)
// print(indexPath)
if let indexPath = indexPath {
if let event = events?[indexPath.row] {
//pops alert vc
var alertController : UIAlertController = UIAlertController(title: event.eventTitle, message: nil, preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
let saveUserToEvent = UIAlertAction(title: "Yes", style: .Default) { _ in
var attendees = [String]()
if let attendeesTmp = event["attendees"] as?[String] {
attendees = attendeesTmp;
}
if let objId = PFUser.currentUser()?.objectId {
var found = false
for objIdd in attendees {
if objIdd == objId {
found = true
break;
}
}
if !found {
attendees.append(objId)
event["attendees"] = attendees;
event.saveInBackground()
}
}
if let user = PFUser.currentUser() {
var eventsAttending = [String]()
if let eventsAttendingTmp = user["eventsToAttend"] as?[String] {
eventsAttending = eventsAttendingTmp;
}
if let eventId = event.objectId {
var found = false
for eventIdd in eventsAttending {
if eventIdd == eventId {
found = true
break;
}
}
if !found {
eventsAttending.append(eventId)
user["eventsToAttend"] = eventsAttending;
user.saveInBackground()
}
}
}
}
alertController.addAction(saveUserToEvent)
}
}
}
Related
I have UIButton for an address in my tableview cell. When I tap on it once; I open google map with the direction no problem. Now, I want to provide the option for long gesture so if you hold your finger on the button, it provides the option to copy the address which is in the title of the button. This is my code:
#IBOutlet weak var addressBtn: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
addLongPressGesture()
}
#objc func longPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == UIGestureRecognizer.State.began {
// how do I make it possible to copy the title of the button here? The address is already inserted as the title of the button
}
}
func addLongPressGesture(){
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gesture:)))
longPress.minimumPressDuration = 0.5
self.addressBtn.addGestureRecognizer(longPress)
}
This is where on one tap it goes to the map with no problem; so I have no issue here but just fyi:
#IBAction func addressClicked(_ sender: Any) {
if (UIApplication.shared.canOpenURL(NSURL(string:"comgooglemaps://")! as URL)) {
let street = order.street.replacingOccurrences(of: " ", with: "+")
let postalCode = order.postalCode.replacingOccurrences(of: " ", with: "+")
if street == "" || order.city == "" || order.province == "" || postalCode == ""{
UIApplication.shared.open(URL(string:"comgooglemaps://?saddr=&daddr=\(order.longitude),\(order.latitude)&directionsmode=driving")! as URL)
} else {
UIApplication.shared.open(URL(string:"comgooglemaps://?saddr=&daddr=+\(street),+\(order.city),+\(order.province),+\(postalCode)&directionsmode=driving")! as URL)
}
} else {
NSLog("Can't use comgooglemaps://")
}
}
Use
let text = addressBtn.currentTitle
or
let text = addressBtn.titleLabel?.text
I figured it out with the following code:
//Create the AlertController and add Its action like button in Actionsheet
let actionSheetControllerIOS8: UIAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
actionSheetControllerIOS8.view.tintColor = AppColors.Blue
let cancelActionButton = UIAlertAction(title: "Cancel", style: .cancel) { _ in
}
actionSheetControllerIOS8.addAction(cancelActionButton)
let saveActionButton = UIAlertAction(title: "Open Google Map", style: .default)
{ _ in
if (UIApplication.shared.canOpenURL(NSURL(string:"comgooglemaps://")! as URL)) {
let street = order.street.replacingOccurrences(of: " ", with: "+")
let postalCode = order.postalCode.replacingOccurrences(of: " ", with: "+")
if street == "" || order.city == "" || order.province == "" || postalCode == ""{
UIApplication.shared.open(URL(string:"comgooglemaps://?saddr=&daddr=\(order.longitude),\(order.latitude)&directionsmode=driving")! as URL)
} else {
UIApplication.shared.open(URL(string:"comgooglemaps://?saddr=&daddr=+\(street),+\(order.city),+\(order.province),+\(postalCode)&directionsmode=driving")! as URL)
}
} else {
NSLog("Can't use comgooglemaps://")
}
}
actionSheetControllerIOS8.addAction(saveActionButton)
let deleteActionButton = UIAlertAction(title: "Copy Address", style: .default)
{ _ in
let address = "\(order.street), \(order.city), \(order.province), \(order.postalCode)"
let pasteBoard = UIPasteboard.general
pasteBoard.string = address
}
actionSheetControllerIOS8.addAction(deleteActionButton)
self.present(actionSheetControllerIOS8, animated: true, completion: nil)
}
I have a page based app, using RootViewController, ModelViewController, DataViewController, and a SearchViewController.
In my searchViewController, I search for an item and then add or remove that Item to an array which is contained in a Manager class(and UserDefaults), which the modelViewController uses to instantiate an instance of DataViewController with the correct information loaded using the dataObject. Depending on whether an Item was added or removed, I use a Bool to determine which segue was used, addCoin or removeCoin, so that the RootViewController(PageView) will show either the last page in the array, (when a page is added) or the first (when removed).
Everything was working fine until I ran into an error which I can not diagnose, the problem is that when I add a page, the app crashes, giving me a "unexpectadely found nil when unwrapping an optional value"
This appears to be the problem function, in the searchViewController 'self.performSegue(withIdentifier: "addCoin"' seems to be called instantly, even without the dispatchque:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
Meaning that In my DataViewController, this function will find nil:
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
I can't find out why it does not wait for completion.
I also get this error about threads:
[Assert] Cannot be called with asCopy = NO on non-main thread.
which is why I try to do the push segue using dispatch que
Here is my searchViewController full code:
import UIKit
class SearchViewController: UIViewController, UISearchBarDelegate {
let selectionLabel = UILabel()
let searchBar = UISearchBar()
let addButton = UIButton()
let removeButton = UIButton()
var filteredObject: [String] = []
var dataObject = ""
var isSearching = false
//Add Button Action.
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
//Remove button action.
#objc func removeButtonActon(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.removeCoin(coin: dataObject)
self.performSegue(withIdentifier: "addCoin", sender: self)
}
searchBar.text = ""
}
//Prepare for segue, pass removeCoinSegue Bool depending on remove or addCoin.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = true
}
} else if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = false
}
}
}
//Remove button action.
#objc func removeButtonAction(sender: UIButton!) {
if Manager.shared.coins.count == 1 {
removeAlert()
} else {
Manager.shared.removeCoin(coin: dataObject)
print(Manager.shared.coins)
print(dataObject)
searchBar.text = ""
self.removeButton.isHidden = true
DispatchQueue.main.async {
self.performSegue(withIdentifier: "removeCoin", sender: self)
}
}
}
//Search/Filter the struct from CGNames, display both the Symbol and the Name but use the ID as dataObject.
func filterStructForSearchText(searchText: String, scope: String = "All") {
if !searchText.isEmpty {
isSearching = true
filteredObject = CGNames.shared.coinNameData.filter {
// if you need to search key and value and include partial matches
// $0.key.contains(searchText) || $0.value.contains(searchText)
// if you need to search caseInsensitively key and value and include partial matches
$0.name.range(of: searchText, options: .caseInsensitive) != nil || $0.symbol.range(of: searchText, options: .caseInsensitive) != nil
}
.map{ $0.id }
} else {
isSearching = false
print("NoText")
}
}
//Running filter function when text changes.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterStructForSearchText(searchText: searchText)
if isSearching == true && filteredObject.count > 0 {
addButton.isHidden = false
dataObject = filteredObject[0]
selectionLabel.text = dataObject
if Manager.shared.coins.contains(dataObject) {
removeButton.isHidden = false
addButton.isHidden = true
} else {
removeButton.isHidden = true
addButton.isHidden = false
}
} else {
addButton.isHidden = true
removeButton.isHidden = true
selectionLabel.text = "e.g. btc/bitcoin"
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Setup the UI.
self.view.backgroundColor = .gray
setupView()
}
override func viewDidLayoutSubviews() {
}
//Hide keyboard
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
//Alerts
func removeAlert() {
let alertController = UIAlertController(title: "Can't Remove", message: "\(dataObject) can't be deleted, add another to delete \(dataObject)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func Duplicate() {
let alertController = UIAlertController(title: "Duplicate", message: "\(dataObject) is already in your pages!", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func max() {
let alertController = UIAlertController(title: "Maximum Reached", message: "\(dataObject) can't be added, you have reached the maximum of 5 coins. Please delete a coin to add another.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
and here is the DataViewController
import UIKit
class DataViewController: UIViewController {
#IBOutlet weak var dataLabel: UILabel!
//Variables and Objects.
//The dataObject carries the chosen cryptocurrencies ID from the CoinGecko API to use to get the correct data to load on each object.
var dataObject = String()
//The DefaultCurrency (gbp, eur...) chosen by the user.
var defaultCurrency = ""
//The Currency Unit taken from the exchange section of the API.
var currencyUnit = CGExchange.shared.exchangeData[0].rates.gbp.unit
var secondaryUnit = CGExchange.shared.exchangeData[0].rates.eur.unit
var tertiaryUnit = CGExchange.shared.exchangeData[0].rates.usd.unit
//Index of the dataObject
var dataIndex = Int()
//Objects
let cryptoLabel = UILabel()
let cryptoIconImage = UIImageView()
let secondaryPriceLabel = UILabel()
let mainPriceLabel = UILabel()
let tertiaryPriceLabel = UILabel()
//Custom Fonts.
let customFont = UIFont(name: "AvenirNext-Heavy", size: UIFont.labelFontSize)
let secondFont = UIFont(name: "AvenirNext-BoldItalic" , size: UIFont.labelFontSize)
//Setup Functions
//Get the index of the dataObject
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
//Label
func setupLabels() {
//cryptoLabel from dataObject as name.
cryptoLabel.text = CGPrices.shared.coinData[dataIndex].name
//Prices from btc Exchange rate.
let btcPrice = CGPrices.shared.coinData[dataIndex].current_price!
let dcExchangeRate = CGExchange.shared.exchangeData[0].rates.gbp.value
let secondaryExchangeRate = CGExchange.shared.exchangeData[0].rates.eur.value
let tertiaryExchangeRate = CGExchange.shared.exchangeData[0].rates.usd.value
let realPrice = (btcPrice * dcExchangeRate)
let secondaryPrice = (btcPrice * secondaryExchangeRate)
let tertiaryPrice = (btcPrice * tertiaryExchangeRate)
secondaryPriceLabel.text = "\(secondaryUnit)\(String((round(1000 * secondaryPrice) / 1000)))"
mainPriceLabel.text = "\(currencyUnit)\(String((round(1000 * realPrice) /1000)))"
tertiaryPriceLabel.text = "\(tertiaryUnit)\(String((round(1000 * tertiaryPrice) / 1000)))"
}
//Image
func getIcon() {
let chosenImage = CGPrices.shared.coinData[dataIndex].image
let remoteImageUrl = URL(string: chosenImage)
guard let url = remoteImageUrl else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
DispatchQueue.main.async {
self.cryptoIconImage.image = UIImage(data: data)
}
}
}.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
// for family in UIFont.familyNames.sorted() {
// let names = UIFont.fontNames(forFamilyName: family)
// print("Family: \(family) Font names: \(names)")
// }
// Do any additional setup after loading the view, typically from a nib.
self.setupLayout()
self.getIndex()
self.setupLabels()
self.getIcon()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.dataLabel!.text = dataObject
view.backgroundColor = .lightGray
}
}
Edit: CGPrices Class with getData method:
import Foundation
class CGPrices {
struct Coins: Decodable {
let id: String
let name: String
let symbol: String
let image: String
let current_price: Double?
let low_24h: Double?
//let price_change_24h: Double?
}
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i + 1) < coins.count { coinsEncoded += "%2C" }
}
print("encoded")
}
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let coinsData = try JSONDecoder().decode([Coins].self, from: data)
self.coinData = coinsData
completion(arr)
} catch let jsonErr {
print("error serializing json: \(jsonErr)")
print(data)
}
}.resume()
}
func refresh(completion: () -> ()) {
defaultCurrency = UserDefaults.standard.string(forKey: "DefaultCurrency")!
completion()
}
}
I figured it out.
The problem was inside my getData method I was not updated the coins array:
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i+1)<coins.count { coinsEncoded+="%2C" }
}
print("encoded")
}
I needed to add this line in getData:
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
//Adding this line to update the array so that the URL is appended correctly.
coins = Manager.shared.coins
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
This would fix the finding nil in the DataViewController, but the app would still crash do to updating UI Elements on a background thread, as the segue was called inside the completion handler of the getData method. to fix this, I used DispatchQue.Main.Async on the segue inside the getData method in the addButton function, to ensure that everything is updated on the main thread, like so:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
print("starting")
CGPrices.shared.getData(arr: true) { (arr) in
print("complete")
print(CGPrices.shared.coinData)
//Here making sure it is updated on main thread.
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
}
}
searchBar.text = ""
}
Thanks for all the comments as they helped me to figure this out, and I learned a lot in doing so. Hopefully this can help someone else in their thought process when debugging, as one can get so caught up in one area of a problem, and forget to take a step back and look to other areas.
i have a problem when i typed my code as is bellow but i got error ?!
i want user if typed code = 4234 on textFields and tap on "ADD" = "https://pastebin.com/raw/4234"
else = any url "http://www.example.com/"
#IBAction func btnPlusPressed(_ sender: UIButton)
{
let alert = UIAlertController(title: "Provider M3U URL", message: "Add Provided URL to add you M3U Plailist", preferredStyle: .alert)
let loginAction = UIAlertAction(title: "ADD", style: .default, handler: { (action) -> Void in
var url = alert.textFields![0]
if (url.text == "535" as String ){
let strURLl : String = "https://pastebin.com/raw/\(url.text!)"
}else{
let strURLl : String = "\(url.text!)"
}
UserDefaults .standard .set(strURLl, forKey: "URL")
let channelsVC = self.storyboard!.instantiateViewController(withIdentifier: "ChannelsViewController") as! ChannelsViewController
channelsVC.strURL = strURLl!
self.navigationController?.pushViewController(channelsVC, animated: true)
})
why error appear like this : (Use of unresolved identifier 'strURLl')
Here you are defining the constant strURLl within the scope of the IF statement.
It means you cannot use it once outside of those { } around it
if (url.text == "535" as String ){
let strURLl : String = "https://pastebin.com/raw/\(url.text!)"
} else {
let strURLl : String = "\(url.text!)"
}
Same problem with the else statement.
Solution
Simply declare the constant outside of the IF scope
let strURLl: String
if (url.text == "535" as String ){
strURLl = "https://pastebin.com/raw/\(url.text!)"
} else {
strURLl = "\(url.text!)"
}
This IBAction brings up a UIAlertController with two text boxes. One box for name and one for age. I'm trying to make it so that the user does not have to enter their age if they don't want to. right now if they don't enter their age the app crashes right by the person.age = Int16(points!)!line saying "Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value". I am trying to use an if else statement stop this. What am I missing though?
#IBAction func addRowBtn(_ sender: Any) {
let alert = UIAlertController(title: "Add Person", message: nil, preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Task"
}
alert.addTextField { (textField) in
textField.placeholder = "Points"
textField.keyboardType = .numberPad
}
let action = UIAlertAction(title: "post", style: .default) { (_) in
let name = alert.textFields?.first!.text!
let age = alert.textFields?.last!.text!
if name != nil && age != nil {
let person = Person(context: PersistenceServce.context)
person.name = name
person.age = Int16(age!)!
PersistenceServce.saveContext()
self.people.append(person)
self.table.reloadData()
}
if name != nil && age == nil {
let person = Person(context: PersistenceServce.context)
person.name = name
person.age = 0
PersistenceServce.saveContext()
self.people.append(person)
self.table.reloadData()
}
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
}
For avoiding unwrap problems I would advise you to do it like that:
let action = UIAlertAction(title: "post", style: .default) { (_) in
if let ageString = alert.textFields?.last?.text,
let age = Int16(ageString),
let name = alert.textFields?.first?.text {
let person = Person(context: PersistenceServce.context)
person.name = name
person.age = age
PersistenceServce.saveContext()
self.people.append(person)
self.table.reloadData()
} else if let name = alert.textFields?.first?.text {
let person = Person(context: PersistenceServce.context)
person.name = name
person.age = 0
PersistenceServce.saveContext()
self.people.append(person)
self.table.reloadData()
}
}
Below is my code to fetch images from icloudkit however now I have to click the UIbutton "getAlbum" 2 times for the images to be displayed in the table view. so did I add tableview.reload()m in a wrong place? please advise
#IBAction func getAlbum(_ sender: AnyObject) {
//Below line to dismiss the keyboard.
textEntercode.resignFirstResponder()
if (self.textEntercode.text == "")
{
let alert = UIAlertController(title: "No Code Entered", message: "Please enter photos code", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil));
//event handler with closure
present(alert, animated: true, completion: nil);
}
else //G
{
// let code: Int64 = Int64(self.textEntercode.text!)!
var photoCode = textEntercode.text!
// convert the characters to upper case before getting data from database
photoCode = photoCode.uppercased()
var count = 0
let Container = CKContainer.default()
let database = Container.publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Photos", predicate: predicate) //Photos is table name in cloudkit server
//-------Fetch the data---------------------
database.perform(query, inZoneWith: nil) { //A
records, error in
if error != nil { //B
print(error?.localizedDescription ?? 10)
} // B
else { //C
count = (records?.count)!
print("countttttt \(count)")
for myrecord in records!
{ //D
self.Saveddata.append(myrecord as CKRecord)
//self.tableview.reloadData()
} //D
let Queue = OperationQueue.main
Queue.addOperation() { //E
self.tableview.reloadData()
} //E
} //C
} //A
} //G