How to include a haptic function on a UI Table View Cell - ios

I have a basic quiz where the uitableview presents four cells. Three cells have wrong answers, and one has a correct answer. I already have included alerts, so this tells the user where he/she has been incorrect and where they are correct.
I have tried to also include a haptic function, however when I run the app on an iPhone 7, the haptic function does not work despite no errors from x code.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return currentQuestion?.answers.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = currentQuestion?.answers[indexPath.row].text
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
guard let question = currentQuestion else {
return
}
let answer = question.answers[indexPath.row]
if checkAnswer(answer: answer, question: question) {
// correct
if let index = gameModels.firstIndex(where: { $0.text == question.text }) {
if index < (gameModels.count - 1){
func correct (_sender: UITableViewCell){
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success)
}
// next question
let nextQuestion = gameModels[index + 1]
print("\(nextQuestion.text)")
currentQuestion = nil
configureUI(question: nextQuestion)
}
else{
// end of game
let alert = UIAlertController(title: "Done", message: "You beat the game", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler: nil))
present(alert, animated: true)
}
}
}
else {
//wrong
let alert = UIAlertController(title: "Wrong", message: "Try again", preferredStyle: .alert)
func wrongTap (_ sender: UITableViewCell){
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
}
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler: nil))
present(alert, animated: true)
}

Try to use impactAccurred instead:
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.prepare()
generator.impactOccurred()

Related

Code Below doesn't result in Logout Button to Work, what is wrong?

Code Below doesn't result in Logout Button to Work, what is wrong?!?
import UIKit
import FirebaseAuth
class ProfileViewController: UIViewController {
// Set Log Out Button
#IBOutlet var tableView: UITableView!
// Data option
let data = ["Log Out"]
// Supply Data Source and Delegate Self
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.delegate = self
tableView.dataSource = self
}
}
// Add an externsion to inform Data Source and Delegate
extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
// Deque row and style text
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = data[indexPath.row]
cell.textLabel?.textAlignment = .center
cell.textLabel?.textColor = .red
return cell
}
// Make logout to occur when user log outs - unhighlight cell
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// Create an alert to Log Out
let actionsheet = UIAlertController(title: "",
message: "",
preferredStyle: .actionSheet)
actionsheet.addAction(UIAlertAction(title: "Log Out",
style: .destructive,
handler: { [weak self] _ in
guard let strongSelf = self else {
return
}
do {
try FirebaseAuth.Auth.auth().signOut()
let vc = LogInViewController()
let nav = UINavigationController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
strongSelf.present(nav, animated: true)
}
catch {
print("Failed to log out")
}
}))
actionsheet.addAction(UIAlertAction(title: "Cancel",
style: .cancel,
handler: nil))
present(actionsheet, animated: true)
}
}
Seems like you have used didDeselectRowAt instead of didSelectRowAt. didSelectRowAt is what gets called when user taps on cell and selects it, didDeselectRowAt gets called when cell is deselected.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let actionsheet = UIAlertController(title: "",
message: "",
preferredStyle: .actionSheet)
actionsheet.addAction(UIAlertAction(title: "Log Out",
style: .destructive,
handler: { [weak self] _ in
guard let strongSelf = self else {
return
}
do {
try FirebaseAuth.Auth.auth().signOut()
let vc = LogInViewController()
let nav = UINavigationController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
strongSelf.present(nav, animated: true)
}
catch {
print("Failed to log out")
}
}))
actionsheet.addAction(UIAlertAction(title: "Cancel",
style: .cancel,
handler: nil))
present(actionsheet, animated: true)
}

How to change the title of an UIAlertAction

How can I change the title of an UIAlertAction when I click the button ?
I want to click that button and from "Enable" to make it "Disable" for example.
I spent a lot of time trying to achieve this but I can't manage to do it.
Here is a small Demo with my issue: https://github.com/tygruletz/ChangeTitleOfAlertAction
Here is my code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.tableFooterView = UIView()
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
showOptions()
}
func showOptions(){
var enable = "Enable"
let disable = "Disable"
let applyOn = UIAlertAction(title: enable, style: .default, handler: { (action: UIAlertAction!) in
enable = disable
})
let actionSheet = configureActionSheet()
actionSheet.addAction(applyOn)
self.present(actionSheet, animated: true, completion: nil)
}
func configureActionSheet() -> UIAlertController {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(cancel)
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad ){
actionSheet.popoverPresentationController?.sourceView = self.view
actionSheet.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
actionSheet.popoverPresentationController?.permittedArrowDirections = []
}
return actionSheet
}
}
And here is a capture of screen:
Thank you if you try to help me !
Please follow below code:
Define property in your UIViewController
var selectedIndexPath:IndexPath!
Add argument in showOptions method
func showOptions(indexPath:IndexPath){
var status = "Enable"
if selectedIndexPath == indexPath{
status = "Disable"
}
let applyOn = UIAlertAction(title: status, style: .default, handler: { (action: UIAlertAction!) in
if self.selectedIndexPath == indexPath{
self.selectedIndexPath = nil
}else{
self.selectedIndexPath = indexPath
}
})
let actionSheet = configureActionSheet()
actionSheet.addAction(applyOn)
self.present(actionSheet, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
showOptions(indexPath: indexPath)
}
Note:
If you are going with this approach, Then you will never faced cell usability issue.

How to execute data inside TableViewCell as a button?

I am creating an app where in the menus are listed inside TableViewCell. The menus are already inside the cell but unfortunately, when I clicked one of the menus, It is not executing the action that the menu should do. For example, i tapped the logout label it does not executing showlogoutDialog. I already used breakpoints but it seems the data inside the table are just plain text. Image below is the sample output of the table. Hope you can give some solution regarding this issue. Thank you.
DoctorMenuTableCell.swift
class DoctorMenuTableCellTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
MoreOptionViewController.swift
private let menuIdentifier = "MenuCell"
class MoreOptionViewController: UIViewController {
#IBOutlet weak var doctorMenuTableView: UITableView!
}
override func viewDidLoad() {
super.viewDidLoad()
self.doctorMenuTableView.dataSource = self
self.doctorMenuTableView.delegate = self
}
//MARK: Function
func showLogoutDialog() {
//create alert
let alert = UIAlertController(title: "Confirmation", message: "Are you sure you want to logout?", preferredStyle: .alert)
//add the actions (button)
alert.addAction(UIAlertAction(title: "No", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (alert) in
self.logout()
}))
self.present(alert, animated: true, completion: nil)
}
func logout() {
NotificationCenter.default.post(name: Notification.Name(deleteUserDataNotificationName), object: nil)
}
}
extension MoreOptionViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return Menu.More.items.count
default: return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = indexPath.row
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: menuIdentifier, for: indexPath) as! DoctorMenuTableCell
cell.titleLabel.text = Menu.More.items[row].value
if row == 1{
cell.titleLabel.textColor = UIColor.red
cell.accessoryType = .none
}
return cell
default:
return UITableViewCell()
}
}
func tableView(_tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0: break
case 1:
switch indexPath.row {
case 0:
let alertController = UIAlertController(title: "Info", message: "No available data", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
case 1: showLogoutDialog()
default: break
}
default: break
}
}
Check your Did Select Method, your cells are under Section 0 and Did Select work for section 1.
Also check isUserInteractionEnabled for both TableView as well as Cell and add breakpoint on didSelectRowAt and check which switch case is working.
Code:
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return Menu.More.items.count
default: return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: menuIdentifier, for: indexPath) as! DoctorMenuTableCell
cell.titleLabel.text = Menu.More.items[row].value
if indexPath.row == 1{
cell.titleLabel.textColor = UIColor.red
cell.accessoryType = .none
}
return cell
default:
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 1: break
case 0:
switch indexPath.row {
case 0:
let alertController = UIAlertController(title: "Info", message: "No available data", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
break
case 1: showLogoutDialog()
break
default: break
}
break
default: break
}
}
Look how many sections you have. You have just one, but inside didSelectRowAt you're showing alerts just if section index is 1 which in you case index never is.
You just need to check if row is 0 or 1
func tableView(_tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
let alertController = UIAlertController(title: "Info", message: "No available data", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
present(alertController, animated: true)
case 1:
showLogoutDialog()
default:
return
}
}
So, if you have only one section, you can also update data source methods. Also you should handle cases that cell isn't in IndexPath with row index 1 since cells are dequeued
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Menu.More.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: menuIdentifier, for: indexPath) as! DoctorMenuTableCell
let row = indexPath.row
cell.titleLabel.text = Menu.More.items[row].value
cell.titleLabel.textColor = .black
cell.accessoryType = .detailButton
if row == 1
cell.titleLabel.textColor = .red
cell.accessoryType = .none
}
return cell
}
First Create Extension if UIViewController as Below,
Create a swift File Named Extension.swift or whatever you want,
extension UIViewController
{
func showLogoutDialog() {
//create alert
let alert = UIAlertController(title: "Confirmation", message: "Are you sure you want to logout?", preferredStyle: .alert)
//add the actions (button)
alert.addAction(UIAlertAction(title: "No", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (alert) in
self.logout()
}))
self.present(alert, animated: true, completion: nil)
}
func logout() {
print("helllllo")
NotificationCenter.default.post(name: Notification.Name(deleteUserDataNotificationName), object: nil)
}
}
Now call showLogoutDialog() on cellForRowAt,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 1: break
case 0:
switch indexPath.row {
case 0:
let alertController = UIAlertController(title: "Info", message: "your data", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
break
case 1: showLogoutDialog()
break
default: break
}
break
default: break
}

Can't reload UITableView

I am trying to design View where i can show elements in sync with my Firebase Database. Every time element in my array changes it gets duplicated. Tried to use
self.tableView.reloadData()
but nothing changes. Tried as well
self.tableView.beginUpdates()
self.tableView.reloadRowsAtIndexPaths(NSArray.init(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic)
self.tableView.endUpdates()
and it didn't work as previously.
I have tried unsuccesfully to perform reloading tableView in main thread using function
performSelectorOnMainThread
I am posting my code for you guys so someone can help me. Im quite new to iOS programming and i can't figure out when to reload data in tableView. Even after reading Apple's Documentation.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let alert = UIAlertController(title: "Akcja", message: "Wybierz akcję", preferredStyle: .ActionSheet)
let changeFieldNumberAction = UIAlertAction(title: "Zmień numer boiska", style: .Default, handler: {(action) in
self.showAlertOfChangingFieldNumber(indexPath)
})
let sendToRefereeAction = UIAlertAction(title: "Wskaż sędziego", style: .Default, handler: {(action) in
//self.championshitTournamentMode = false
})
let cancelAction = UIAlertAction(title: "Anuluj", style: .Cancel, handler: nil)
alert.addAction(cancelAction)
alert.addAction(changeFieldNumberAction)
alert.addAction(sendToRefereeAction)
self.presentViewController(alert, animated: true, completion: nil)
}
func showAlertOfChangingFieldNumber(indexPath: NSIndexPath) {
let fieldNumberAlert = UIAlertController(title: "Numer boiska", message: "Wpisz numer boiska", preferredStyle: .Alert)
fieldNumberAlert.addTextFieldWithConfigurationHandler({(textField: UITextField!) -> Void in
textField.placeholder = "Numer boiska"
textField.keyboardType = UIKeyboardType.NumberPad
})
let saveAction = UIAlertAction(title: "Zapisz", style: .Default, handler: {(action) -> Void in
let fieldNumberTextField = fieldNumberAlert.textFields![0] as UITextField
let fieldNumber = Int(fieldNumberTextField.text!)
self.updateGameFieldNumber(fieldNumber!, indexPath: indexPath)
})
let cancelAction = UIAlertAction(title: "Anuluj", style: .Cancel, handler: nil)
fieldNumberAlert.addAction(cancelAction)
fieldNumberAlert.addAction(saveAction)
self.presentViewController(fieldNumberAlert, animated: true, completion: nil)
}
func updateGameFieldNumber(fieldNumber: Int, indexPath: NSIndexPath){
let gameKey = games[indexPath.row].key
let ref = FIRDatabase.database().reference().child("games").child(gameKey)
games[indexPath.row].fieldNumber = fieldNumber
ref.updateChildValues(games[indexPath.row].toAnyObject() as! [NSObject : AnyObject])
//self.games.removeAtIndex(indexPath.row+1)
self.tableView.beginUpdates()
self.tableView.reloadRowsAtIndexPaths(NSArray.init(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic)
self.tableView.endUpdates()
}
And my tableView delegate functions and function made for filling my array based on Firebase's database looks like that:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! GameViewCellTableViewCell
if games[indexPath.row].fieldNumber == 0 {
cell.fieldNumberLabel.text = "-"
} else {
cell.fieldNumberLabel.text = String(games[indexPath.row].fieldNumber)
}
cell.firstParticipantLabel.text = games[indexPath.row].firstParticipant
cell.secondParticipantLabel.text = games[indexPath.row].secondParticipant
cell.gameImage.image = UIImage(named: "tenisBall")
if games[indexPath.row].completed != true {
cell.backgroundColor = UIColor.init(red: 1, green: 109.0/255, blue: 95.0/255, alpha: 1)
} else {
cell.backgroundColor = UIColor.init(red: 160/255, green: 1, blue: 86/255, alpha: 1)
}
return cell
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.games.count
}
func getGames() {
let ref = FIRDatabase.database().reference().child("games")
ref.observeEventType(.Value, withBlock: {snapshot in
for item in snapshot.children {
let gameItem = GameItem(snapshot: item as! FIRDataSnapshot)
if(gameItem.tournamentName == self.tournamentName) {
self.games.append(gameItem)
}
}
self.tableView.reloadData()
})
}
It gets duplicated because instead of updating the value in your "games" array, you append the modified value to the array. This happens in the "getgames()" method.
inside the
func getGames()
after
self.games.append(gameItem)
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})

Swift - UITableView won't add data

I have a UITableView and when I add data, it will only allow me to add one row in the UITableview even though I return goals.count for the amount of rows.
This is what I have.
#IBAction func myAddGoal(sender: AnyObject) {
let ac = UIAlertController(title: "Enter a Goal", message: "It can be anything you want!", preferredStyle: .Alert)
ac.addTextFieldWithConfigurationHandler(nil)
let submit = UIAlertAction(title: "Submit", style: .Default) { [unowned self, ac] (action: UIAlertAction!) in
let goal = ac.textFields![0] as UITextField
self.submitGoal(goal.text)
}
ac.addAction(submit)
ac.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Destructive, handler: nil))
presentViewController(ac, animated: true, completion: nil)
}
func submitGoal(goal: String){
if goal == "" {
println("textfield is empty")
} else {
goals.insert(goal, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
myTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
println(goals)
}
}
I am able to add only ONE goal and it will animate into the UITableView. If I want to add another goal, it will not let me. The app crashes when I hit submit for a second goal entry.
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return goals.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let object = goals[indexPath.row]
cell.textLabel!.text = object
mySwitch.on = false
mySwitch.tag = indexPath.row as Int
mySwitch.addTarget(self, action: "UISwitchUpdated", forControlEvents: UIControlEvents.TouchUpInside)
cell.selectionStyle = .None
cell.accessoryView = mySwitch
return cell
}

Resources