UIAlert view triggered from CustomCell Class // Swift - ios

I have a TableViewCell populated with CustomCells. In the customCells I have a button which I want to trigger an UIAlert.
Here is my code for the button in the CustomCell Class:
#IBAction func anzahlButton(sender: UIButton) {
let alert = UIAlertController(title: "Bitte gib Deine gewünschte Anzahl an:", message: nil, preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler {
(tf:UITextField!) in
tf.keyboardType = .NumberPad
tf.addTarget(self, action: "textChanged:", forControlEvents: .EditingChanged)
}
func handler(act:UIAlertAction!) {
let tf = alert.textFields![0] as UITextField
let addItem = "\(tf.text)"
var fixedToDoItems = ""
anzahlText.setTitle("\(addItem)", forState: .Normal)
//println("User entered \(addItem), tapped \(act.title)")
}
alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: handler))
(alert.actions[1] as UIAlertAction).enabled = false
TableViewController().presentViewController(alert, animated: true, completion: nil)
}
When I hit the button I get this alert: Warning: Attempt to present <UIAlertController: 0x7fc7f0cbc5c0> on <MyApp.TableViewController: 0x7fc7f0c965f0> whose view is not in the window hierarchy!
I deed some resaerch on the debug-alert, but couldn't find an answer that is in swift and works for me... ;-)
Any ideas?
THX
//Seb

In your line TableViewController().presentViewController, TableViewController() actually creates a new instance of the view controller TableViewController. This instance is not the one currently displayed on the screen. Which is why you get the error that the view is not part of the window hierarchy.
In order to fix that move the func anzahlButton(sender: UIButton) to the TableViewController file and then connect it to the button through cellForRowAtIndexPath function. remove the #IBAction part since we are no longer connecting the button through the interface builder.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
// Your Code
cell.button.addTarget(self, action: "anzahlButton:", forControlEvents: UIControlEvents.TouchUpInside)
}
Then change the line
TableViewController().presentViewController(alert, animated: true, completion: nil)
to
self.presentViewController(alert, animated: true, completion: nil)

Related

How do I change a UIButton background picture when an action in a UIAlertController is clicked?

#IBOutlet weak var myPictureOutlet: UIButton!
#IBAction func myPictureAction(_ sender: Any) {
let alert = UIAlertController(title: "Chnage Pic", message: "to Change the pic, press the button below", preferredStyle: . alert)
alert.addAction(UIAlertAction(title: "change picture", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
self.myPictureOutlet.setBackgroundImage(ButtonImage, for: UIControlState.normal)
}))
self.present(alert, animated: true, completion: nil)
}
My goal is to:
create an UIAlertController when a UIButton (with a default picture) is clicked
then, when a user clicks on the button in the UIAlertController, it changes the UIButton picture.
I am having a little trouble with this task. The UIAlert is working when I touch the UIButton, however the UIButton background is not changing. Please review my code and tell me if something is wrong.
Your code should actually work. You don't need this line though:
alert.dismiss(animated: true, completion: nil)
UIAlertController will remove it for you after you press the alert button.
Also to improve your code just refer to the IBAction sender like this:
guard let button = sender as? UIButton else {
print("Ooops, not a button!")
return
}
button.setBackgroundImage(ButtonImage, for: UIControlState.normal)

Add a condition to a modal push segue [duplicate]

This question already has answers here:
Perform segues when I want
(2 answers)
Closed 4 years ago.
I use a navigation controller and modal show segues.
I want to block the back button of my second view controller if [condition] isn't true, for exemple by adding an alert "You can't go back until [condition]" when the user press the back button.
Don't know if it's possible, if someone know how to solve it !
Thanksss
You need to add a custom action for your back bar button item.
#IBAction func backPressed(_ sender: UIBarButtonItem) {
if !myCondition {
let alert = UIAlertViewController(title: "Alert", message: "Please fulfill the condition")
let action = UIAlertAction(title: "OK", .default)
self.present(alert, animated: true)
return
}
navigationController?.popViewController(animated: true)
}
Or you can just use normal target action from code.
myBarButton.addTarget(self, selector: #selector(backButtonPressed(_:)))
Sorry for incorrect syntax.
Need back button image "ic-menu-back-primary"
override func viewDidLoad() {
super.viewDidLoad()
// Nav Back Button
self.navigationItem.hidesBackButton = true
let backButton = UIBarButtonItem(image: #imageLiteral(resourceName: "ic-menu-back-primary"), style: .plain, target: self, action: #selector(back(_:)))
navigationItem.leftBarButtonItem = backButton
}
#IBAction func back(_ sender: Any?) {
let alert = UIAlertController(title: nil, message: "You can't go back until [condition]", preferredStyle: .alert)
let resume = UIAlertAction(title: "Cancel", style: .cancel)
let cancel = UIAlertAction(title: "Exit", style: .default) { (action) in
self.navigationController?.popViewController(animated: true)
}
alert.addAction(resume)
alert.addAction(cancel)
present(alert, animated: true)
}

Custom uiview with button inside alertController does not work in iOS

I am trying to create an alert controller which has a custom view inside it. The custom view is loaded from xib file. The custom view contains a uiswitch. The problem is that its click event is not being triggered and from UI switch is not turned on/off on click. Here is the code I am trying to work on:
This is the button click event of my view controller to present alert:
#IBAction func btnClicked(_ sender: UIButton) {
let alertController = UIAlertController(title: "\n\n\n\n\n\n", message: "", preferredStyle: UIAlertControllerStyle.alert)
let customView=Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)!.first! as! CustomView
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {(alert: UIAlertAction!) in print("cancel")})
alertController.addAction(cancelAction)
let somethingAction = UIAlertAction(title: "Something", style: .default, handler: {(alert: UIAlertAction!) in print("something")})
alertController.addAction(somethingAction)
alertController.view.addSubview(customView)
self.present(alertController, animated: true, completion:{})
}
This is the code inside CustomView class:
class CustomView: UIView {
#IBAction func switchClicked(_ sender: UISwitch) {
print("switch cliked")
}
}
My CustomView.xib file has the referencing layout properly set. It just has one uiswitch. The CustomView.xib has its size set to 'freeform'(not sure if it matters). I also tried setting isUserInteractionEnabled to false or true at various places for CustomView and/or alertController.view after searching for similar issues but nothing works.
You should be capturing the valueChanged control event but if you want to do the value change programmatically via click I suppose you could do:
class CustomView: UIView {
#IBAction func switchClicked(_ sender: UISwitch) {
sender.setOn(!sender.isOn, animated: true)
}
}
I think xib view will triger method only when same class has been loaded in memory. In your case this might be an issue . To test this u can add custom view in same class in which u are showing alert .

Dismiss UIAlertViewController by tapping outside

I want to dismiss my UIAlertViewController by tapping outside of the UIAlertViewController, on tapping on black screen space. I've tried this:
self.presentViewController(alertViewController, animated: true, completion:{
alertViewController.view.superview?.userInteractionEnabled = true
alertViewController.view.superview?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.alertClose(_:))))
})
but it just closes if I tapped on UIAlertViewController. But I want outside, where half-blacked screen tapped.
Is it possible?
UPDATE
#IBAction func shareButtonTapped(sender: UIButton) {
let alertViewController = UIAlertController(title: "Share on social networks", message: "Where do you want to share?", preferredStyle: .ActionSheet)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissAlertView:")
self.view.addGestureRecognizer(tapGestureRecognizer)
let facebookAction = UIAlertAction(title: "Facebook", style: .Default) { (alert: UIAlertAction) -> Void in
}
let twitterAction = UIAlertAction(title: "Twitter", style: .Default) { (alert: UIAlertAction) -> Void in
}
let cancelAction = UIAlertAction(title: "Cancel", style: .Destructive) { (alert: UIAlertAction) -> Void in
}
alertViewController.addAction(facebookAction)
alertViewController.addAction(twitterAction)
alertViewController.addAction(cancelAction)
self.presentViewController(alertViewController, animated: true, completion:{
alertViewController.view.superview?.userInteractionEnabled = true
alertViewController.view.superview?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.alertClose(_:))))
})
}
AlertClose function:
func alertClose(gesture: UITapGestureRecognizer) {
self.dismissViewControllerAnimated(true, completion: nil)
}
If you are using ActionSheet style there is no need to do this. Because when you tap on half-blacked screen view controller automatically will be dismissed.
But if you want to use with Alert style do following (without: alertViewController.view.superview?.userInteractionEnabled = true) :
self.presentViewController(alertViewController, animated: true, completion: {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.alertClose(_:)))
alertViewController?.view.superview?.addGestureRecognizer(tapGestureRecognizer)
})
Try to add UITapGestureRecognizer to your UIWindow class like
view.window.addGestureRecognizer()
implement UIGestureRecognizerDelegate method shouldReceiveTouch
and check your tapped view
Hope this help

swift: Popover on button in table view cell always display on the first cell

I am making a table view in Swift which each cell contains a button.
When a button is tapped, a popover will display on that button.
My Problem is, no matter which cell I click the button inside, popover always display on the first cell.
Please see attached images for more understanding.
Below is my code in tableviewcontroller class. I use tag to detect touching on the button.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "Cell01"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as? UICell01
//when tap share button
cell!.shareButton.tag = indexPath.row
cell!.shareButton.addTarget(self, action: "shareAction:", forControlEvents: .TouchUpInside)
return cell!
}
#IBAction func shareAction(sender: UIButton) {
//Create Action Sheet to be menu
let alert:UIAlertController = UIAlertController(title: "Share on", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
{
UIAlertAction in
}
// Add the actions
alert.addAction(cancelAction)
// Present the controller
if UIDevice.currentDevice().userInterfaceIdiom == .Phone
{
self.presentViewController(alert, animated: true, completion: nil)
}
else //iPad
{
var popover:UIPopoverController?=nil
popover = UIPopoverController(contentViewController: alert)
popover!.presentPopoverFromRect(sender.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
I think the problem is at the last line. "sender" that is passed to function is the button in the first cell only. How do I solve the problem?
You are referencing sender.frame relative to its own cell, and not incorporating the cell's frame relative to the UITableView. Check sender's superview.
Try to declare the shareAction inside a customer UITableViewCell class and assing it to the cell. Like below
import UIKit
class CellCustome: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func shareAction(sender: UIButton) {
//Create Action Sheet to be menu
let alert:UIAlertController = UIAlertController(title: "Share on", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
{
UIAlertAction in
}
// Add the actions
alert.addAction(cancelAction)
// Present the controller
if UIDevice.currentDevice().userInterfaceIdiom == .Phone
{
self.presentViewController(alert, animated: true, completion: nil)
}
else //iPad
{
var popover:UIPopoverController?=nil
popover = UIPopoverController(contentViewController: alert)
popover!.presentPopoverFromRect(sender.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Thanks all for enlighten me.
I can simply solve by changing the last line to
popover!.presentPopoverFromRect(sender.frame, inView: sender.superview!, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
Now the popover balloon displays on the correct button I touch.
But I still do not quite understand the inView argument.
If it is self.view, the popover balloon displays on the first cell.
if is is sender.superview, the popover balloon displays on the correct cell.
Update for Swift4:
In the View Controller:
popover?.sourceView = sender.superview
popover?.sourceRect = sender.frame
self.present(popoverContent, animated: true, completion: nil)

Resources