swift: segue on button click - ios

I want to move to the next controller on button click with using segue. I need to get number of press button in next controller.
This is code from my controller:
import UIKit
class ViewController2: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tblTable: UITableView!
var buttonTitles = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"]
override func viewDidLoad() {
super.viewDidLoad()
tblTable.delegate = self
tblTable.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return buttonTitles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "buttoncell") as! ButtonCell
let buttonTitle: String = buttonTitles[indexPath.row]
cell.btnButton.setTitle(buttonTitle, for: .normal)
cell.btnButton.tag = indexPath.row
cell.btnButton.addTarget(self, action: #selector(self.buttonClick(button:)), for: .touchUpInside)
cell.selectionStyle = .none
return cell
}
#objc func buttonClick(button: UIButton) -> Void {
print("btnButton clicked at index - \(button.tag)")
button.isSelected = !button.isSelected
if button.isSelected {
button.backgroundColor = UIColor.green
} else {
button.backgroundColor = UIColor.yellow
}
}
}
class ButtonCell: UITableViewCell {
#IBOutlet var btnButton: UIButton!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
btnButton.backgroundColor = UIColor.green
} else {
btnButton.backgroundColor = UIColor.yellow
}
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
if highlighted {
btnButton.backgroundColor = UIColor.green
} else {
btnButton.backgroundColor = UIColor.yellow
}
}
}
How to solve the problem it with my code?

It's very simple.
Follow these steps to create segue from your tableview cell button (click).
Open your storyboard layout (view controller)
Add new (destination) view controller.
Select your button.
Press & hold control ctrl button from keyboard and drag mouse cursor from your button to new (destination) view controller.
Now add following code to your source view controller file (source code)
-
override func performSegue(withIdentifier identifier: String, sender: Any?) {
print("segue - \(identifier)")
if let destinationViewController = segue.destination as? <YourDestinationViewController> {
if let button = sender as? UIButton {
secondViewController.<buttonIndex> = button.tag
// Note: add/define var buttonIndex: Int = 0 in <YourDestinationViewController> and print there in viewDidLoad.
}
}
}
Another way to handle the same.

You need to use performSegueWithIdentifier("yourSegue", sender: sender) to segue on an event. This takes in your segue identifier in place of "yourSegue".
This will go in the func that you call when the user presses the button. If you need to send the amount of button clicks to the new View Controller then I would do something similar to this:
let secondViewController = segue.destination as! ViewController
secondViewController.buttonClicks = myButtonClicks

Related

Save user settings using user default swift

I'm working on an application where users can view terms and like or dislike terms.
I'm stack on saving user settings from the table view using user default. I want to save when users click the like or dislike buttons, and when they run the app again the button stays filled
I have a table view cell that contains an outlet for the button and action
import UIKit
class TerminologistTVCell: UITableViewCell {
#IBOutlet weak var btnLike: UIButton!
#IBOutlet weak var btnDislike: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
lconfigureUI()
dconfigureUI()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func lconfigureUI(){
let thumbsdown = UIImage(systemName: "hand.thumbsdown")
let thumbsdownfilled = UIImage(systemName: "hand.thumbsdown.fill")
btnDislike.setImage(thumbsdown, for: .normal)
btnDislike.setImage(thumbsdownfilled, for: .selected)
}
func dconfigureUI(){
let thumbsup = UIImage(systemName: "hand.thumbsup")
let thumbsupfilled = UIImage(systemName: "hand.thumbsup.fill")
btnLike.setImage(thumbsup, for: .normal)
btnLike.setImage(thumbsupfilled, for: .selected)
}
#IBAction func btnLike(_ sender: UIButton) {
sender.isSelected.toggle()
if (sender.isSelected){
btnDislike.isSelected = false
}else{
btnDislike.isSelected = false
}
}
#IBAction func btnDislike(_ sender: UIButton) {
sender.isSelected.toggle()
if (sender.isSelected){
btnLike.isSelected = false
}else{
btnLike.isSelected = false
}
}}
And the ViewController to view the terms and save settings. I tried to save the setting in cellForRow it worked, but when I clicked on the button, it saved for all cells(the button is filled in all cells), not for a cell that I pressed. I want to save for pressed cell
class TerminologistVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var termaArray = MDTerms()
let termName = ""
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return termaArray.arabicTerm.count
}
let userDefaults = UserDefaults.standard
let btnLikePressed = "Likepressed"
let btnDisLikePressed = "DisLikepressed"
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TerminologistTVCell
cell.textLabel?.text = self.termaArray.arabicTerm[indexPath.row]
cell.btnLike.tag = indexPath.row
cell.btnLike.addTarget(self, action: #selector(likeTerm(sender:)), for: .touchUpInside)
cell.btnDislike.tag = indexPath.row
cell.btnDislike.addTarget(self, action: #selector(dislikeTerm(sender:)), for: .touchUpInside)
if userDefaults.bool(forKey: btnLikePressed){
cell.btnLike.isSelected = true
}else{
cell.btnLike.isSelected = false
}
if userDefaults.bool(forKey: btnDisLikePressed){
cell.btnDislike.isSelected = true
}else{
cell.btnDislike.isSelected = false
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
#objc
func likeTerm(sender: UIButton){
print("cell index = \(sender.tag)")
if sender.isSelected{
userDefaults.set(true, forKey: btnLikePressed)
}else{
userDefaults.set(false, forKey: btnLikePressed)
}
}
#objc
func dislikeTerm(sender: UIButton){
print("cell index = \(sender.tag)")
if sender.isSelected{
userDefaults.set(true, forKey: btnDisLikePressed)
}else{
userDefaults.set(false, forKey: btnDisLikePressed)
}
}
My application looks like
ViewController
You are using only two keys in UserDefault which are btnDisLikePressed and btnLikePressed, and clearly you will always get the same values for all cells with all terms. Instead use the termaArray.arabicTerm[indexPath.row] (or in your case cell.textLabel?.text) as the key in UserDefaults.

tableview sent me an error when I tried to present a uiview in front of it

I'm new with Xcode and Swift, following a tutorial and I found a problem when I called a UIView in front of a tableview so the user can create something new
NOTE: I already tried what this link shows with no luck to resolve my issue
I am using Xcode 11.3.1 and Swift
This is my code
Channel Model
import Foundation
struct Channel : Decodable {
public private(set) var channelTitle: String!
public private(set) var channelDescription: String!
public private(set) var id: String!
}
Class ChannelCell
import UIKit
class ChannelCell: UITableViewCell {
// Outlets
#IBOutlet weak var channelName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
self.layer.backgroundColor = UIColor(white: 1, alpha: 0.2).cgColor
} else {
self.layer.backgroundColor = UIColor.clear.cgColor
}
}
func configureCell(channel: Channel) {
let title = channel.channelTitle ?? ""
channelName.text = "#\(title)"
}
}
Channel View Controller
import UIKit
class ChannelVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Outlets
#IBOutlet weak var loginBtn: UIButton!
#IBOutlet weak var userImg: CircleImage!
#IBOutlet weak var tableView: UITableView!
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self <<<<<<<<< here I get the error message ***
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
tableView.dataSource = self
self.revealViewController()?.rearViewRevealWidth = self.view.frame.size.width - 60
NotificationCenter.default.addObserver(self, selector: #selector(ChannelVC.userDataDidChange(_:)), name: NOTIF_USER_DATA_DID_CHANGE, object: nil)
}
override func viewDidAppear(_ animated: Bool) {
setupUserInfo()
}
When I pressed the add channel button comes the problem
#IBAction func addChannelPressed(_ sender: Any) {
if AuthService.instance.isLoggedIn {
let addChannel = ChannelVC()
addChannel.modalPresentationStyle = .custom
present(addChannel, animated: true, completion: nil)
} else {
performSegue(withIdentifier: TO_LOGIN, sender: nil)
}
}
#IBAction func loginBtnPressed(_ sender: Any) {
if AuthService.instance.isLoggedIn {
let profile = ProfileVC()
profile.modalPresentationStyle = .custom
present(profile, animated: true, completion: nil)
} else {
performSegue(withIdentifier: TO_LOGIN, sender: nil)
}
}
#objc func userDataDidChange(_ notif: Notification) {
setupUserInfo()
}
func setupUserInfo() {
if AuthService.instance.isLoggedIn {
loginBtn.setTitle(UserDataService.instance.name, for: .normal)
userImg.image = UIImage(named: UserDataService.instance.avatarName)
userImg.backgroundColor = UserDataService.instance.returnUIColor(components: UserDataService.instance.avatarColor)
} else {
loginBtn.setTitle("Login", for: .normal)
userImg.image = UIImage(named: "menuProfileIcon")
userImg.backgroundColor = UIColor.clear
}
}
// Protocols for UITableViewDataSource
// # of sections
// # rows in the section
// function to setup the cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "channelCell", for: indexPath) as? ChannelCell {
I double check the reusable identifier is OK
let channel = MessageService.instance.channels[indexPath.row]
cell.configureCell(channel: channel)
return cell
} else {
return UITableViewCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if MessageService.instance.channels.count == 0 {
tableView.setEmptyView(title: "Message!", message: "You don´t have any channel, create a new one")
}
return MessageService.instance.channels.count
}
}
this is the view I want to show when I click on the addChannel function
view to present
and this is the debug area
debug area
You are making a very common mistake. The line
let addChannel = ChannelVC()
creates a new instance of the controller which is not the instance in the storyboard. Therefore the outlets are not connected and the code crashes.
Replace it with (adjust the identifier accordingly)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let addChannel = storyboard.instantiateViewController(withIdentifier: "ChannelVC") as! ChannelVC
or create a segue.

Create several buttons

I want to create 10 buttons in the ViewController. These buttons move the user to the next ViewController. If I use a storyboard, do I have to create 10 buttons or is there an easier way to solve the problem?
It should also satisfy following conditions:
my button into cell won't be gray or another colour. But I need to my button will be selected and change colour.
If I use the tableView and press the button, the selected cell fills up with a gray color. I want to select only the button. (Tableview should not show gray color for selection)
Here is sample code as solution to your problem (it's working according to your requirement, just copy and paste in your view controller)
import UIKit
class ViewController2: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tblTable: UITableView!
var buttonTitles = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"]
override func viewDidLoad() {
super.viewDidLoad()
tblTable.delegate = self
tblTable.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return buttonTitles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "buttoncell") as! ButtonCell
let buttonTitle: String = buttonTitles[indexPath.row]
cell.btnButton.setTitle(buttonTitle, for: .normal)
cell.btnButton.tag = indexPath.row
cell.btnButton.addTarget(self, action: #selector(self.buttonClick(button:)), for: .touchUpInside)
cell.selectionStyle = .none
return cell
}
#objc func buttonClick(button: UIButton) -> Void {
print("btnButton clicked at index - \(button.tag)")
button.isSelected = !button.isSelected
if button.isSelected {
button.backgroundColor = UIColor.green
} else {
button.backgroundColor = UIColor.yellow
}
}
}
class ButtonCell: UITableViewCell {
#IBOutlet var btnButton: UIButton!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
btnButton.backgroundColor = UIColor.green
} else {
btnButton.backgroundColor = UIColor.yellow
}
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
if highlighted {
btnButton.backgroundColor = UIColor.green
} else {
btnButton.backgroundColor = UIColor.yellow
}
}
}
And Snapshot of storyboard layout with tableview and cell interface design
Here is result (working behavior of button) in simulator
I think, this is enough to solve your problem.

Action button not working within UITable

I have been through few of these tutorial and I cant seem to call my function via button click. I have followed this guy's tutorial but nothing works.
My tableView currently working just great, all Im doing now is adding a button so I could pass data to another view but my button click is not calling its function.
//class IncomeFeedCell: UITableViewCell:
#IBOutlet var weak button: UIButton!
View Contoller:
// class IncomeFeedVC: UIViewController, UITableViewDelegate, UITableViewDataSource:
// viewDidLoad:
tableView.delegate = self
tableView.dataSource = self
// cellForRowAt indexPath
let cell = self.tableView.dequeueReusableCell(withIdentifier: "IncomeFeedCell") as! IncomeFeedCell
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
cell.configureCell(income: income)
return cell
Now the function to call when tapped:
func buttonPressed(){
print("Tapped")
}
Simulator:
Have I missed something simple?
Edit:
Apologies for all who and tried to help and got downvoted because I left out more vital information. All this is inside my viewDidLoad in IncomeFeedVC:
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
DataService.ds.REF_INCOMES.queryOrdered(byChild: "date").observe(.value, with: { (snapshot) in
/**
* Sorting the object before looping over
* Info here: http://stackoverflow.com/questions/41416359/sort-firebase-data-with-queryordered-by-date
*/
guard let incomeSnap = snapshot.value as? [String:AnyObject] else{
return
}
let sorted = incomeSnap.sorted{($0.0.value["date"] as! Double) > ($0.1.value["date"] as! Double)}
for snap in sorted {
if let incomeDict = snap.value as? [String: AnyObject] {
let key = snap.key
let income = Income(incomeId: key, incomeData: incomeDict)
self.incomes.append(income)
}
}
self.tableView.reloadData()
})
My ViewController:
class ViewController: UIViewController , UITableViewDelegate,UITableViewDataSource{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! as! TestTableViewCell
cell.bttn.addTarget(self, action: #selector(clickme(sender:)), for: .touchUpInside)
return cell
}
func clickme(sender: UIButton) {
print("cliked")
}
}
My UITableViewCell
import UIKit
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var bttn: UIButton!
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
}
}
Its working fine in my case.
Can you remove the "self" in your
let cell = self.tableView.dequeueReusableCell(withIdentifier: "IncomeFeedCell") as! IncomeFeedCell line.
your button tapped function should be like this:
func buttonPressed(sender: UIButton)
{
let buttonTag = sender.tag// you can check tag like this
}
Can you please try
cell.button.addTarget(self, action: #selector(IncomeFeedVC.buttonPressed(_:)), for: .touchUpInside)
Also below function should be implemented in IncomeFeedVC class
func buttonPressed(_ sender: AnyObject){
print("Tapped")
}

Why are my buttons preferences/parameters resetting when I change view?

I have this iOS application where the value from cells in a tableview is used to set the preferences/parameters on my UIButtons.
When the button is pressed I send with it the tag ID to the tableview, when the user press a cell it collects both text and image and return it (along with the tag ID) back to the main view.
This successfully changes the parameters on the button with the corresponding button tag ID, but when I now press a new button to do the same procedure it resets the first button changes (clears the image and text) and just applies changes to the new button that is pressed.
This is the main view controller class:
class ViewController: UIViewController {
var recievedItem: ChosenItem?
var imageToButton: UIImage?
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
func AddNew() {
performSegueWithIdentifier("addNew", sender: nil)
}
#IBAction func loadItem(sender: UIButton!) {
performSegueWithIdentifier("itemList", sender: sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "itemList"){
let tableViewController : TableViewController = segue.destinationViewController as! TableViewController
tableViewController.buttonTag = sender!.tag
}
}
#IBAction func play(sender: UIButton) {
print("Jeg har fått \(recievedItem!.chosenWord)")
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.translucent = true
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Open", style: .Plain, target: self, action: "AddNew")
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "AddNew")
if(recievedItem != nil){
imageToButton = UIImage(data: recievedItem!.chosenImage)
switch recievedItem!.chosenButton{
case 0:
button1.setBackgroundImage(imageToButton, forState: .Normal)
button1.setTitle(recievedItem!.chosenWord, forState: .Normal)
case 1:
button2.setBackgroundImage(imageToButton, forState: .Normal)
button2.setTitle(recievedItem!.chosenWord, forState: .Normal)
case 2:
button3.setBackgroundImage(imageToButton, forState: .Normal)
button3.setTitle(recievedItem!.chosenWord, forState: .Normal)
default:
print("No buttonTag recieved")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is the tableview class:
class TableViewController: UITableViewController {
var words = [Words]()
var chosenItem: ChosenItem!
var buttonTag: Int!
override func viewDidLoad() {
super.viewDidLoad()
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context: NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest(entityName: "Words")
request.returnsObjectsAsFaults = false
do {
words = try context.executeFetchRequest(request) as! [Words]
} catch {
print("Unresolved error")
abort()
}
print("Her er også button tag \(buttonTag)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.words.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let itemWord = self.words[indexPath.row]
cell.textLabel?.text = itemWord.word
cell.imageView?.image = UIImage(data: itemWord.image!)
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(self.words[indexPath.row].word!)
chosenItem = ChosenItem()
chosenItem.chosenButton = buttonTag
chosenItem.chosenWord = self.words[indexPath.row].word!
chosenItem.chosenImage = self.words[indexPath.row].image!
performSegueWithIdentifier("backToMain", sender: chosenItem)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "backToMain"){
let mainViewController : ViewController = segue.destinationViewController as! ViewController
let data = sender as! ChosenItem
mainViewController.recievedItem = data
}
}
}
This is the model class that holds the data:
class ChosenItem: NSObject {
var chosenButton: Int!
var chosenWord: String!
var chosenImage: NSData!
}
The problem is this line:
performSegueWithIdentifier("backToMain", sender: chosenItem)
That is not how you return from a pushed-or-presented view controller to the view controller that pushed-or-presented it! What you are doing is creating a completely new view controller, and that's why the chosen button is not set; it is a different view controller with a different chosen button, namely none because this view controller has just come into existence.
Not only will this mess things up with the chosen button, but eventually your app will crash because you are creating one view controller on top of another every time you go forward and back, and eventually you'll run out of memory.
The way to get back from a pushed view controller is popViewController. The way to get back from a presented view controller is dismissViewController.
If you know what you're doing, you can instead use a special non-segue segue called an unwind segue, but it doesn't sound like you're ready to do that.

Resources