How to pass a Parameter to the addTarget method of button in swift - uibutton

I have a CollectionViewCell and i am working on cellForItemAt method of it. I have a button in each Collection view cell which defines to 3 different section in my CollectionView.
I am adding below code to set the button target:
cell.buttonView.addTarget(self, action: #selector(buttonPressed(sender:)), for: UIControlEvents.touchUpInside)
Now, i have created a new method: #objc func buttonPressed(sender: UIButton) where i am adding a title to the UIAlertController like:
let alertController = UIAlertController(title: , message: "Below actions are", preferredStyle: .actionSheet)
So basically, each time any button is tapped in the cell, the Alert should open and also each time the title will be different for it.
How do i pass the title here dynamically?

If the title to be passed is button's title, then you can simply do this :-
#objc func buttonPressed(sender: UIButton){
let title = sender.title(for: .normal)
}
And if it is some other data in section, you can use tags on your buttons, and setting them to indexPath.row :-
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.buttonView.tag = indexPath.row
}
And in your Button's action, access button's tag
#objc func buttonPressed(sender: UIButton){
let objectIndex = sender.tag
let object = yourArray[objectIndex]
let title = object.title
}

Related

Create UICollectiionViewCell based on UIView

I am trying to have a UICollectionView that holds different UIViews as its cells. Is this possible or do I have to make them UICollectionViewCells?
I don't have an objective-c example, but you should be able to get the concept from the code example below.
An example how you can create a cell that wraps a UIView and is more reusable
class ProfileView: UIView {
var imageView: UIImageView!
var name: UILabel!
}
class ProfileCollectionViewCell: UICollectionViewCell {
let profileView = ProfileView()
init() {
super.init()
configureConstraints()
}
func configureConstraints() {
// use a handy extension you've already built
contentView.addSubView(profileView)
profileView.pinToEdges(of: contentView)
}
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let row = self.objects[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "someId" for: indexPath) as? ProfileCollectionViewCell
cell?.profileView.imageView.image = row["image"]
cell?.profileView.name.text = row["name"]
return cell
}
note: you may need to manage 'resetting the cells state' before it gets reused with something like:
override prepareForReuse() {
super.prepareForReuse()
profileView.imageView.image = nil
profileView.name.text = ""
}
You have to return UICollectionViewCells. UICollectionView don't accept UIViews.
What you can do is create a generic UICollectionViewCell that can embed any UIView.
The reason is because collection view cells have specific composition for layout and recycling.
Also, you add child in your UIView directly on the view itself, collection view cells have a contentView, like UITableViewCells.

Downpicker not appearing in UITableView

I am attempting to use the Darkseal Downpicker inside a UITableView. I am programatically adding a UITextField and then adding the Downpicker as follows:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var textField = UITextField()
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.selectionStyle = .none
textField.text = "TEST"
textField.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(170), height: CGFloat(30))
var dp: DownPicker!
let test1 = ["test one", "test two"]
dp = DownPicker(textField: textField, withData: test1)
cell.contentView.addSubview(dp)
return cell
}
If I just add the textfield:
cell.contentView.addSubview(textField)
Then it displays the text field. However if I add the DownPicker as shown above:
cell.contentView.addSubview(dp)
I get nothing displayed, just an empty tableview. Any help understanding why the DownPicker is not displayed would be appreciated. Thanks.
You need to add both ur UITextField and ur DownPicker into the View. If it's anything like the UIPickerView then your DownPicker will only show when you select the UITextField. Also you need to add your DownPicker to the view rather then the cell.
view.addSubView(dp)
EDIT
"It takes any UITextField already present in your code (including those added to a Storyboard):" - https://github.com/Darkseal/DownPicker
EDIT 2
From looking at the docs what you want to do is add yourself as the observer on the DataPicker by doing the following inside the cellForRowAt func
dp.addTarget(self, action: #selector(dp_selected(sender:)), for: .valueChanged)
You want to add a func the class with cellForRowAt func inside it
func dp_selected(sender: AnyObject) {
guard let dp = sender as? DropDown else { return }
if let textfield = dp.getTextField() as? UITextField {
guard let indexPath = tableView.indexPath(for: textfield.superview) else { return }
}
}
This function will check if the sender is of type DropDown class and if it is will try to get the UITextField that it's attached to and if that is returned it will attemt to get the IndexPathat which it's being used at this can be nil if the cell is no longer visible on the screen but hopefully that will never be the case and have added a check to make sure it exists.

Segue on DidSelectRowAtIndexPath from Custom DataSource/Delegate Swift

My setup:
`UITableViewController` (ComboViewController)
-> Several Static Cells
-> One Static Cell contains a dynamic `tableView`
I need to use a custom Delegate/DataSource because the dynamic tableView is embedded in the Static TableView within the TableViewController
This custom Delegate/DataSource looks like this:
class DataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
// class variables
override init() {
super.init()
// initialize variables
}
//some data source/ delegate methods like number of rows, cellForRowAtIndexPath
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var indexedCombos: NSDictionary?
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let comboVC: ComboInfoViewController = storyboard.instantiateViewControllerWithIdentifier("ComboInfo") as! ComboInfoViewController
comboVC.doSegue()
}
}
Within ComboViewController I have this:
class ComboInfoViewController: UITableViewController {
func doSegue() {
self.performSegueWithIdentifier("tosingle", sender: combListTable)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "tosingle" {
//do stuff
}
}
}
If the segue is modal I get this error:
Warning: Attempt to present SingleProductViewController on ComboInfoViewController whose view is not in the window hierarchy!
If the segue is push, then the prepareForSegue method gets called, but the viewController does not push! What is happening?
I've searched and searched. But I have no idea what could be resulting in this behavior.
When you create the ComboInfoViewController instance with this line,
let comboVC: ComboInfoViewController = storyboard.instantiateViewControllerWithIdentifier("ComboInfo") as! ComboInfoViewController
You're creating a new instance that is not the one you have on screen, and never will be, so that's why you get the error. It is very important that you understand this concept; understanding how view controllers are created, and how to get pointers to ones that already exist is fundamental to iOS programming.
However, in this case you don't even need to get a pointer to the one on screen, because you should connect the segue directly from the cell (the dynamic prototype), which means you won't need any code to execute it. You can delete the didSelectRowAtIndexPath method, and the doSegue method. You only need to implement prepareForSegue. If you need to pass information to the next controller based one which row was touched, you can do it like below. The table view controller code should now look like this (this is an update of the code in my answer to this question, Swift: TableView within Static UITableViewCell),
class ComboInfoViewController: UITableViewController {
#IBOutlet weak var staticTableView: UITableView!
#IBOutlet weak var dynamicTableView: UITableView!
var dataSource = DataSource()
override func viewDidLoad() {
super.viewDidLoad()
dynamicTableView.dataSource = dataSource
dynamicTableView.delegate = dataSource
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row != 1 {
return 44
}else{
return 250 // the second cell has the dynamic table view in it
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "tosingle" {
var cell = sender as! UITableViewCell
var indexPath = dynamicTableView.indexPathForCell(cell)
var dataPoint = dataSource.theData[indexPath!.row] // theData is the array used to populate the dynamic table view in the DataSource class
// pass dataPoint to the next view controller which you get from segue.destinationviewController
println(dataPoint)
}
}
}

Pass Data to next ViewController Using UIBarButtonItem in Swift

I have a UITableView that allows multiple selections. I'd like to pass the selections to the next "Q2ViewController" when user clicks the rightBarButtonItem "Next".
Note: I'm not using Storyboard. After searching through, I haven't found a solution without using storybaord.
var options = ["breakfast", "lunch", "dinner", "dessert"]
var selected = -1
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Q1View"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .Done, target: self, action: "didTapNext:")
self.tableView.allowsMultipleSelection = true
var nib = UINib(nibName: "OptionCell", bundle: nil)
tableView?.registerNib(nib, forCellReuseIdentifier: "OptionCellIdentifier")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.options.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("OptionCellIdentifier", forIndexPath: indexPath) as OptionCell
cell.textLabel?.text = self.options[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
Anywhere in your code you must create the "next" view controller. Either in your App Delegate or directly in the view controller shown above.
If you create the view controller outside the above one you will have to pass the above controller a reference to the destination view controller.
If you create the view controller inside the above one save a reference to it in an instance variable.
Or create a method that creates the VC on pressing the "Next" button and pushes the VC to the navigation controller.
You can use UITableView's indexPathsForSelectedRows() or similar to get the selection and then call a method/set a instance variable on the destination view controller.
But seriously, why don't you use storyboards?

Get button's row in view based table

How do you get the row for a button in a view based table when you click the button? The row is not selected when the button is clicked, but I found that if you log sender.superview.superview in the button's action method, I get: NSTableRowView: 0x1001b3a90 - row: 2. So, the row is there in the log, but I don't know how to get at it programmatically. I have the button's action method in a subclass of NSTableCellView.
-[NSTableView rowForView:] says this in its documentation:
This is typically needed in the action method for an NSButton (or NSControl) to find out what row (and column) the action should be performed on.
In Swift 5.1 -
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if let checkBoxCell = tableView.makeView(withIdentifier:NSUserInterfaceItemIdentifier(rawValue: "<ColumnIdentifier>"), owner: self) as! NSButton? {
checkBoxCell.tag = row;
checkBoxCell.target = self;
checkBoxCell.action = #selector(TableViewService.checkBoxAction)
return checkBoxCell
}
return nil
}
#objc func checkBoxAction(button:NSButton){
print(button.tag);
}
Here I'm giving you a simple example. In this I'm adding a UIButton in content view. When I clicked on button I call a Method and there I get Row number and call as I required
//Adding a button
UIButton *btnBuyer=[UIButton buttonWithType:UIButtonTypeCustom];
btnBuyer.frame=CGRectMake(238, 10, 26, 32);
btnBuyer.tag=indexPath.row;
[btnBuyer setImage:[UIImage imageNamed:#"buyIcon.png"] forState:UIControlStateNormal];
[btnBuyer addTarget:self action:#selector(goBuyTab:)forControlEvents:UIControlEventTouchUpInside];
[cellNew.contentView addSubview:btnBuyer];
And When User Clicks on this I got Id from the following method
-(void)goBuyTab:(UIButton *)sender{
NSLog(#"after click buy button function called goBuyTab");
NSLog(#"sender.tag in goBuyTab : %d",sender.tag);
int selectedRow=sender.tag;// This is row number
}
Hope this is what you required.
I share this code for Swift 4.
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
........
but.action = #selector(ViewController.buttonAction)
.........
}
#objc func buttonAction(but: NSButton){
let tablerow: NSTableRowView = but.superview?.superview as! NSTableRowView;
let index = table?.row(for: tablerow);
print("\(index));
}
in osx you can use this in your button cell action method in the controller:
- (IBAction)yourMethod:(NSButtonCell *)sender {
NSInteger selected = [yourTableView selectedRow];
NSLog(#"sender sends :%ld", selected);
}
you need to set an outlet from the controller to the NSTableView.