Radio Button using swift4 in iOS - ios

I want to create multiple choice question app in which i want to display four options with one selectable answer using radio button but I am not able to understand how to do it using array , Any help will be appreciated
!!!
I herreby attached my code --
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var topicLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
var dictionary1 = [Int:String]()
var dictionary2 = [Int:Array<String>]()
override func viewDidLoad() {
dictionary1 = [0:"Whether you have experienced Pricking-pain, Desquamation,itching or dry skin sensation during seasonal alternate.", 1:"Whether your skin apt to flush( Redness) in hot humid environment ", 2:"Whether your skin has multiple disernible dilated capillaries.", 3:"whether you have once been diagnosed atopic dermatitis or seborrheic dermatitis."]
dictionary2 = [0:["Never","Seldom","Usually","Always"],1:["Never","Seldom","Usually","Always"],2:["Never","Seldom","Usually","Always"],3:["Yes", "No"]]
titleLabel.text = "Fill Skin Type Survey Form "
titleLabel.textColor = UIColor.black
topicLabel.text = "Are You with sensitive skin type ?"
topicLabel.font = UIFont.boldSystemFont(ofSize: 18)
let homeNib = UINib(nibName: "DemoTableViewCell", bundle: nil)
self.tableView.register(homeNib, forCellReuseIdentifier: "DemoTableViewCell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dictionary1.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:DemoTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "DemoTableViewCell", for: indexPath) as! DemoTableViewCell
// FOR FIRST DICTIONARY
cell.questionLabel.text = dictionary1[indexPath.row]
cell.questionLabel.font = UIFont.boldSystemFont(ofSize: 16)
// FOR SECOND DICTIONARY
cell.optionsLabel.text = dictionary2[indexPath.row]?.joined(separator: " ")
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 350.0
I want to display dictionary 2 along with radio button for selecting an option , here is screenshot of my expected output

You can get the support from so many GitHub Libraries:
https://github.com/DavydLiu/DLRadioButton
https://github.com/onegray/RadioButton-ios (No longer actively maintained)
https://github.com/alhazmy13/RadioButtonSwift3
https://github.com/xxi511/radioButton-swift
https://github.com/VenkateshYadavP/PVRadioButton
https://github.com/thegoal/ISRadioButton
Or else if u want to do that programmatically using UIButton lemme know i can share the code with you.
#IBAction func btnRadioCategoryClicked(_ sender: UIButton) {
for button in btnALLTerritory {
if sender.tag == button.tag{
button.isSelected = true;
button.setImage(#imageLiteral(resourceName: "ic_Radio_filled"), for: .normal)
}else{
button.isSelected = false;
button.setImage(#imageLiteral(resourceName: "ic_Radio_Empty"), for: .normal)
}
}
}
You need to take the Group Outlets of all your UIButton and make some logic like this if you prefer to do that programmatically. Or also you can make an array to store the id of Selected button tag. And use like to select and Unselect a UIButton with that logic
Hope this help.
Simple Demo for Radio Button
You can download the demo code from here Demo Of Radio Button

By referring Abhirajsinh solution, I have created two buttons in interface and connected to controller. Make sure that you have changes tag value in attribute for button1(Tag = 0) and button2(Tag = 1). Also remove selected images in interface for each button.
class AddWorkExperienceViewController: UIViewController {
// MARK: - IBOutlets
var btnALLTerritory = [UIButton]()
#IBOutlet weak var doButton: UIButton!
#IBOutlet weak var dontButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
btnALLTerritory = [doButton,dontButton]
}
// MARK: - Actions
#IBAction func btnRadioCategoryClicked(_ sender: UIButton) {
for button in btnALLTerritory {
if sender.tag == button.tag{
button.isSelected = true;
button.setImage(#imageLiteral(resourceName: "radioButton-selected"), for: .normal)
}else{
button.isSelected = false;
button.setImage(#imageLiteral(resourceName: "radioButton-unselected"), for: .normal)
}
}
}
}

If you are using tableview for options then save the state of the each cell radio button in Model Array. Once use click on radio button, change the state of radio button and reload table view. Custom UI will be the best way to implement.

Related

iOS tableview cell button selection strange behavior

In tableview, each cell has a bookmark image button. When tapping on it, it becomes in a selected state and the image (red bookmark icon) is shown. I have delegate method to add information in cells with selected buttons to an array:
protocol CustomPoetCellDelegate {
func cell(_ cell: CustomPoetCell, didTabFavIconFor button: UIButton)
}
class CustomPoetCell: UITableViewCell {
#IBOutlet weak var poetNameLabel: UILabel!
#IBOutlet weak var verseCountLabel: UILabel!
#IBOutlet weak var periodLabel: UILabel!
#IBOutlet weak var iconButton: UIButton!
var delegate: CustomPoetCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
iconButton.setImage(UIImage(named: "icons8-bookmark-50-red.png"), for: .selected)
iconButton.setImage(UIImage(named: "icons8-bookmark-50.png"), for: .normal)
iconButton.isSelected = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func favIconTapped(_ sender: UIButton) {
delegate?.cell(self, didTabFavIconFor: sender)
}
}
In tableViewController:
func cell(_ cell: CustomPoetCell, didTabFavIconFor button: UIButton) {
if !button.isSelected {
let indexPathRow = tableView.indexPath(for: cell)!.row
if isFiltering() {
favoritePoets.append(searchResults[indexPathRow])
button.isSelected = true
} else {
favoritePoets.append(poets[indexPathRow])
button.isSelected = true
}
} else {
button.isSelected = false
favoritePoets = favoritePoets.filter { $0.arabicName != cell.poetNameLabel.text}
}
}
isFiltering() method checks if searchBar is in process.
Now, if isFiltering() is false, everything is fine, but if isFiltering() is true (that is, searching for a name in tableView), when I tap on a specific cell button to select it and then click cancel to dismiss the searchBar, icons for other cells are also selected, in spite of that only the one I tapped is added to the array. When navigating back and forth from the view, wrong selections are gone and only the right one is selected.
Any idea on what's going on?
Thanks in advance.
The problem is that the UITableView re-uses cell for optimized scroll performance. Hence it re-uses the cell which is not in view anymore with a new cell about to be displayed. Therefore you need to set the state of the cell when ever it is updated/reused.
The following function is called everytime. So you need to set cell properties for selected or non-selected state over here.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.indexPath = indexPath //Create an indexPath property in your cell and set it here
//This will be required when you call the delegate method
//configure your cell state
return cell
}
You can update your models in favoritePoets array by changing the specific property in delegate method that you are already calling for e.g
favoritePoets[indexPath.row].selected = true or false
For accessing the indexPath you need to set it as mentioned in above code. And then pass it as an argument in your delegate method. Now this updated property will help you set your state in cellForRowAt function.
Hope it helps :). Feel free to comment.

How to change image of imageview in tableview custom cell with pagination.

I am using a tableview in an app in which I have used pagination. The request is sent to the server and it returns items in batches of size 10. everything is working fine till now. Now I have an imageview in my tableview cells (custom). I want that when the image of that imageview toggles when user taps on it. I tried this thing in the following way:
TableviewController:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell : AdventureTableViewCell = tableView.dequeueReusableCell(withIdentifier: "adventureCell" , for: indexPath) as? AdventureTableViewCell else {
fatalError("The dequeued cell is not an instance of AdventureViewCell.")
}
cell.adventureName.text = adventureList[indexPath.row]
cell.amountLabel.text = "\(adventurePriceList[indexPath.row])$"
cell.favouriteButtonHandler = {()-> Void in
if(cell.favouriteButton.image(for: .normal) == #imageLiteral(resourceName: "UnselectedFavIcon"))
{
cell.favouriteButton.setImage(#imageLiteral(resourceName: "FavSelectedBtnTabBar"), for: .normal)
}
else
{
cell.favouriteButton.setImage(#imageLiteral(resourceName: "UnselectedFavIcon"), for: .normal)
}
}
}
CustomCell:
class AdventureTableViewCell: UITableViewCell {
#IBOutlet weak var adventureName: UILabel!
#IBOutlet weak var adventureImage: UIImageView!
#IBOutlet weak var amountLabel: UILabel!
#IBOutlet weak var favouriteButton: UIButton!
#IBOutlet weak var shareButton: UIButton!
var favouriteButtonHandler:(()-> Void)!
var shareButtonHandler:(()-> Void)!
override func awakeFromNib() {
super.awakeFromNib()
adventureName.lineBreakMode = .byWordWrapping
adventureName.numberOfLines = 0
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
override func prepareForReuse() {
adventureImage.af_cancelImageRequest()
adventureImage.layer.removeAllAnimations()
adventureImage.image = nil
}
#IBAction func favouriteButtonTapped(_ sender: Any) {
self.favouriteButtonHandler()
}
Now the problem which I am facing is that if user taps the first the imageview on any cell it changes its image, but along with that every 4th cell changes it image.
For example, if I have tapped imageview of first cell its image is changed but image of cell 5, 9, 13... also get changed.
What is wrong with my code? Did I miss anything? It is some problem with indexPath.row due to pagination, but i don't know what is it exactly and how to solve it. I found a similar question but its accepted solution didn't work for me, so any help would be appreciated.
if you need to toggle image and after scrolling also that should be in last toggle state means you need to use an array to store index position and toggle state by comparing index position and scroll state inside cellfoeRowAtIndex you can get the last toggle state that is one of the possible way to retain the last toggle index even when you scroll tableview otherwise you will lost your last toggle position
if self.toggleStatusArray[indexPath.row]["toggle"] as! String == "on"{
cell.favouriteButton.setImage(#imageLiteral(resourceName: "FavSelectedBtnTabBar"), for: .normal)
} else {
cell.favouriteButton.setImage(#imageLiteral(resourceName: "UnselectedFavIcon"), for: .normal)
}
cell.favouriteButtonHandler = {()-> Void in
if self.toggleStatusArray[indexPath.row]["toggle"] as! String == "on"{
//Assign Off status to particular index position in toggleStatusArray
} else {
//Assign on status to particular index position in toggleStatusArray
}
}
Hope this will help you
Your code looks OK, I see just one big error.
When u are setting dynamic data (names, images, stuff that changes all the time) use func tableView(UITableView, willDisplay: UITableViewCell, forRowAt: IndexPath) not cellForRowAt indexPath.
cellForRowAt indexPath should be used for static resources, and cell registration.
If u are on iOS 10 + take a look at prefetchDataSource gonna speed things up a loot, I love it.
https://developer.apple.com/documentation/uikit/uitableview/1771763-prefetchdatasource
Small example:
here u register the cell, and set up all the stuff that is common for all the cells in the table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "adventureCell" , for: indexPath)
cell.backgroundColor = .red
return cell
}
here adjust all the stuff that is object specific
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.nameLabel.text = model[indexPath.row].name
// also all the specific UI stuff goes here
if model[indexPath.row].age > 3 {
cell.nameLabel.textColor = .green
} else {
cell.nameLabel.textColor = .blue
}
}
You need this because cells get reused, and they have their own lifecycle, so you want to set specific data as late as possible, but you want to set the generic data as less as possible ( most of the stuff you can do once in cell init ).
Cell init is also a great place for generic data, but u can not put everything there
Also, great thing about cell willDisplay is the that u know actual size of the frame at that point

Interactive header with buttons of tableView

I have a tableView which have one two cells.
first for section header having 3 buttons, acting as a check box,and
second cell with simple labels to populate the data.
Now what I want is to update the tableView's second cells data with a section header like shown in screenshot below. But I'm unable to get the clickable action for these buttons on the same header.
What I tried so far is:
first I used tapReconizer for all three of them, it was working but it was not changing the image of the button (which is imp, as through image it is acting like a checkbox)
then I made the action outlet for all three now they are working as in but I'm unable to update data from the cell's custom class, below is the code
class SavedCallHeader : UITableViewCell{
var checkBox_PlannedisOn:Bool = false
var checkBox_BothisOn:Bool = true
var checkBox_unPlannedisOn:Bool = false
let defaults = UserDefaults.standard
#IBOutlet weak var PlannedBoxBtn: UIButton!
#IBOutlet weak var BothBoxBtn: UIButton!
#IBOutlet weak var unPlannedBoxBtn: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func PlannedCheckBox(_ sender: UIButton) {
if checkBox_PlannedisOn == false {
self.PlannedBoxBtn.setImage(UIImage(named: "Checked Checkbox-26.png"), for: UIControlState.normal)
checkBox_PlannedisOn = true
print("i'm finally here proper click!")
// self.fetchData() // wont' work here as it is in the main VC Class
// tableView.reloadData() // won't work here as well
}else {
self.PlannedBoxBtn.setImage(UIImage(named: "Unchecked Checkbox-26.png"), for: UIControlState.normal)
print("i'm finally heress proper click!")
checkBox_PlannedisOn = false
}
}
I want to update and refresh data on every time the user select/deSelect the checkBox. Below is my main code:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let identifierHeader:String = "SavedCallHeader"
let headerCell = tableView.dequeueReusableCell(withIdentifier: identifierHeader) as! SavedCallHeader
/* let tapPlanned = UITapGestureRecognizer(target: self, action: #selector(self.respondToSwipeGestureP))
let tapBoth = UITapGestureRecognizer(target: self, action: #selector(self.respondToSwipeGestureB))
let tapUnPlanned = UITapGestureRecognizer(target: self, action: #selector(self.respondToSwipeGestureU))
headerCell.PlannedBoxBtn.addGestureRecognizer(tapPlanned)
headerCell.BothBoxBtn.addGestureRecognizer(tapBoth)
headerCell.unPlannedBoxBtn.addGestureRecognizer(tapUnPlanned)
*/
return headerCell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier:String = "savedcall_cell"
let cell:SavedCalls_Cell = self.tableView.dequeueReusableCell(withIdentifier:identifier ) as! SavedCalls_Cell!
if(drname.count > 0){
//Fetching data from table view and then reload
}
Creating callBack like this in your cell class
var callBackForReload : ((Bool) -> ())?
#IBAction func PlannedCheckBox(_ sender: UIButton) {
// when you call this call back it will excute in where you acces it.
// I pass bool value for examble. you will pass whtever datatype you want
self.callBackForReload!(true)
}
The below code execute after CallBack code executed in your cell class
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let identifierHeader:String = "SavedCallHeader"
let headerCell = tableView.dequeueReusableCell(withIdentifier: identifierHeader) as! SavedCallHeader
headerCell.callBackForReload = { [weak self] (isCalled) -> Void in
//This will call when the call back code excuted in your cell class
// in The isCalled variable you will get the value from cell class
// You will reload your changed value here
}
return headerCell
}
You have to do below things,
Instead of UITableViewCell, you have to take Button Outlets in ViewController.
When User clicked on any cell you can get on which button and cell user clicked. Ref
Now reload that cell/section as user clicked. Ref

UIButton inside table not triggering

Hello I'm trying to figure out how to call a UIButton inside a custom cell within a UItable in storyboard. At the moment I have a library that creates a sidemenu working just fine (more info here) and I can see the button I placed when I launch the simulator. However, when I click on the button the action is not triggered, can you please guide me as to how I can achieve this?
Important to note that the table was create entirely in storyboard.
My work in progress code within TopratedVC.swift to get the button to trigger the action:
override func viewDidLoad() {
super.viewDidLoad()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewVibrantCell") as! CellClassMenu
cell.sendFeedBackBtn.tag = indexPath.row
cell.sendFeedBackBtn.addTarget(self, action: "sendFeedBackBtnAction:", forControlEvents: .TouchUpInside)
cell.contentView.userInteractionEnabled = false //tried with true as well, no difference
cell.bringSubviewToFront(cell.sendFeedBackBtn)
cell.userInteractionEnabled = true
return cell
}
func sendFeedBackBtnAction(sender: UIButton){
print("sendFeedBackBtnAction tapped")
}
My UITableViewVibrantCell.swift file contains the following:
import UIKit
class UITableViewVibrantCell: UITableViewCell {
#IBOutlet var sendFeedBackBtn: UIButton!
}
My sndFeedBackBtn has a referencing outlet to UITableViewVibrantCellsendFeedBackBtn which has a class of UITableViewVibrantCell. What am I doing wrong? Thank you.
What it looks like in simulator:
In your post, you show a UITableViewVibrantCell class, and dequeue a cell with the "UITableViewVibrantCell" identifier, but cast it as CellClassMenu?
Anyhow, it would be better practice to create a cell delegate for actions, and let your controller decide the implementation, rather than adding a target every time the cell is dequeued. You can do that like so:
UITableViewVibrantCell
import UIKit
protocol UITableViewVibrantCellDelegate: NSObjectProtocol {
func buttonPressed(sender: UIButton)
}
class UITableViewVibrantCell: UITableViewCell {
var delegate: UITableViewVibrantCellDelegate?
#IBOutlet var feedbackButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
feedBackButton.addTarget(self, action: #selector(self.buttonPressed(_:)), forControlEvents: .TouchUpInside)
}
func buttonPressed(sender: UIButton) {
delegate?.buttonPressed(sender)
}
}
TopratedVC
class TopratedVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewVibrantCell") as! UITableViewVibrantCell
cell.delegate = self
return cell
}
// MARK: - UITableViewVibrantCellDelegate
func buttonPressed(sender: UIButton) {
print("feedbackButton tapped")
}
}
Selectors ("sendFeedBackBtnAction:") can't pass parameters. And a param isn't needed in the sendFeedBackBtnAction function since you're calling it only for this button. So change I'd change it to simply...
func sendFeedBackBtnAction()
then I'd also recommend changing your selector to a more updated swift version...
cell.sendFeedBackBtn.addTarget(self, action: #selector(sendFeedBackBtnAction), forControlEvents: .TouchUpInside)

Swift Custom Swipe Table View Cell issue in dynamic

I have a list of comments. If it is my user own comment, when the user swipes to left to right , delete option will be enabled.After deleting the comments, it will reload the table. A user can add a new comment on the same page, that time also, it will reload the table.
I am using this repository and using this
Problem is that;
It is working but with few bugs.Let's explain the case. If it is not my comment also show the delete option when I swipe because I have added the new comment and delete some comments. in that case previous added swipe cell is not removed.I think that is the case. Could not realise it.
When I scroll the comment, it shows the delete option in the wrong manner. I have checked user id with the swipe. Please guide me. It was working in the normal way. Now current requirement was to show delete icon.
This is my code :
class CustomCommentCellTableViewCell: PKSwipeTableViewCell {
#IBOutlet weak var timelineLabel : UILabel!
#IBOutlet weak var commentMsg : UILabel!
#IBOutlet weak var commenterName : UILabel!
#IBOutlet weak var profName : UILabel!
#IBOutlet weak var proIcon : UIImageView!
var commentCount : Int?;
var userID : String?
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configureCell(row:Int, userID: String,msg: String) {
// self.lblTitle.text = strToSet
self.userID = userID;
self.commentCount = row;
print("row ==== :\(row) =====:\(msg)")
print(":\(userID) ==loged=:\(Globals.sharedInstance.userId) =====:\(userID == Globals.sharedInstance.userId)")
if(userID == Globals.sharedInstance.userId){
self.addRightViewInCell()
}else{
self.resetCellState()
}
}
func addRightViewInCell() {
//Create a view that will display when user swipe the cell in right
let viewCall = UIView()
viewCall.backgroundColor = UIColor(red: 254.0/255.0, green: 53.0 / 255.0, blue: 63.0 / 255.0, alpha: 1.0)
viewCall.frame = CGRectMake(0, 0,CGRectGetHeight(self.frame)+20,CGRectGetHeight(self.frame))
//Add a label to display the call text
//Add a button to perform the action when user will tap on call and add a image to display
let btnCall = UIButton(type: UIButtonType.Custom)
btnCall.frame = CGRectMake((viewCall.frame.size.width - 40)/2,viewCall.frame.size.height/2,40,40)
btnCall.setImage(UIImage(named: "ico_delete"), forState: UIControlState.Normal)
btnCall.addTarget(CommentViewController(), action: #selector(CommentViewController.confirmDelete), forControlEvents: UIControlEvents.TouchUpInside)
viewCall.addSubview(btnCall)
//Call the super addRightOptions to set the view that will display while swiping
super.addRightOptionsView(viewCall)
}
func callButtonClicked(){
//Reset the cell state and close the swipe action
self.resetCellState()
}}
after deleting and adding the new comment, I always call the reload table.
Please guide me
Can't you use default iOS delete for this. But it is not recommended to add images to this default delete button only text can change.
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true // if deletable else false
}
func tableView(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath) -> String? {
return "Del"
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle = .Delete {
// perform the delete and reload table
}
}
Try overriding cell's - (void)prepareForReuse method and remove this additional view there.

Resources