How to add action to a programmatically-made button? - swift

Since I was having issues creating a button via storyboard, I went about initiating a right nav bar button through code - found via this question How can I go back to the initial view controller in Swift?. This code is meant to take me back to my root view controller.
So here is the code as it stands at the moment.
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(getter: UIDynamicBehavior.action))
self.navigationItem.rightBarButtonItem = button1
func button() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
I was under the impression that if I were to change the
action: #selector(getter: action)
I would be able to create a function following this button initialization like so
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
However, I am greeted with the "Use of local variable 'action' before its declaration'". I do not understand why this interpretation would not perform/why I would have to establish the action variable when its only use is a function name? Any help would be appreciated.
Update 1: Current Code
override func viewDidLoad() {
super.viewDidLoad()
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(action))
self.navigationItem.rightBarButtonItem = button1
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
Update 1: Also had to use a different function action to go back to the original view controller.
self.navigationController!.popToRootViewController(animated: true)

It should be #selector(action). And you can define function within a function. Move action outside of the viewDidLoad function. Try this.
override func viewDidLoad() {
super.viewDidLoad()
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(action))
self.navigationItem.rightBarButtonItem = button1
}
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}

Related

Swift 3 - Reload NavigationController Button Image

I have a navigation controller view, with an embedded collectionview controller. I have a right bar button added programmatically, with an image added to it as follows:
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "greyCircle").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleGrayCircleButton))
I would like a user that clicks on this button, to be able to change the image from grey to silver. I have the button handler set up to switch from grey to silver with each press:
self.navigationItem.rightBarButtonItem?.image = UIImage(named: "greyCircle")
However, the image only changes briefly before switching back. I have tried to save the color of the image as a 'flag' variable, i.e. if it is currently grey, turn to silver, in order to make the change persist, however, the image continually resets to whatever it is loaded with (either with viewDidLoad or viewWillAppear).
Is there anyway to refresh/reload the navigationController tab bar after each click and make it persist, along with the images for the bar buttons?
#
EDIT: Following is full button handler.
func handleFavoritePress(){
print("Favorites pressed")
if(!returnTrueIfInFavorites(objectName: readString(object: self.patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)){
// Add to favorites
Favorites_Names.append(readString(object: self.patientRecord!, key: "name"))
Favorites_Types.append(LIST_CoreDataBaseObjects.Patients)
// Let user know
let patientName = readString(object: self.patientRecord!, key: "name")
let alertController = UIAlertController(title: "Alert!", message: "\(patientName) has been added to your favorites.", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
var rootViewController = UIApplication.shared.keyWindow?.rootViewController
if let navigationController = rootViewController as? UINavigationController {
rootViewController = navigationController.viewControllers.first
}
if let tabBarController = rootViewController as? UITabBarController {
rootViewController = tabBarController.selectedViewController
}
rootViewController?.present(alertController, animated: true, completion: nil)
self.navigationItem.rightBarButtonItem?.image = UIImage(named: "savedFavorite_1")
}else{
self.navigationItem.rightBarButtonItem?.image = UIImage(named: "favorite_2")
deleteFromFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)
}
for favorite in Favorites_Names{
print("Favorites are currently: \(patient)")
}
}
And other functions:
override func viewWillAppear(_ animated: Bool) {
collectionView?.backgroundView = setBackgroundImage(imageName: "whiteBackground")
navigationItem.title = "Patient"
if(returnTrueIfInFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)){
setupNavBarButtonsIsFavorited()
}else{
setupNavBarButtonsNotFavorited()
}
}
func setupNavBarButtonsNotFavorited(){
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "favorite_2").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}
func setupNavBarButtonsIsFavorited(){
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "savedFavorite_1").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}
The answer to your question lies within this two LIST_CoreDataBaseObjects.Patients, returnTrueIfInFavorites or the patientRecord variable.
Either LIST_CoreDataBaseObjects.Patients did not get updated on your function handleFavoritePress's code
/* this code */
Favorites_Names.append(readString(object: self.patientRecord!, key: "name"))
Favorites_Types.append(LIST_CoreDataBaseObjects.Patients)
/* this code */
Because from what you've explained in the comments section, if I understood it correctly, is that when you tap the button the image changes, and then you navigate to a different view, but then when you navigate back, viewDidAppear(_:) and viewWillAppear(_:) will be triggered.
Which in the code block below I will explain
// this function will be triggered everytime you navigate into this view (because it has appeared)
override func viewWillAppear(_ animated: Bool) {
/* don't forget to call super.viewWillAppear(true) */
super.viewWillAppear(true)
/* don't forget to call super.viewWillAppear(true) */
collectionView?.backgroundView = setBackgroundImage(imageName: "whiteBackground")
navigationItem.title = "Patient"
// the problem lies here
/* the function `(returnTrueIfInFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients))` is always returning one thing which is why your image returns back to its previous state */
if (returnTrueIfInFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)) {
setupNavBarButtonsIsFavorited() // only one of these gets called
} else {
setupNavBarButtonsNotFavorited() // only one of these gets called
}
}
func setupNavBarButtonsNotFavorited() {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "favorite_2").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}
func setupNavBarButtonsIsFavorited() {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "savedFavorite_1").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}
All of these or you're just not updating the variable self.patientRecord!'s value (I haven't seen any setting in your code relating to this variable)

xcode swift nulpad with next button not working

I am relatively new to coding so please bear with me. I have written a simple app which has 6 text fields that need to completed before an action button is pressed to perform a mathematical calculation.
I have everything done but i can't get my next button to move to the next text field. I have added the tool bar and the done and next button but when i press the next button the app crashes and i get a "Thread 1: signal SIGABRT".
I have been through the internet and I am struggling to see how do do it. I would have thought this would be pretty straight forward.
I just can't make the next button perform the action to the next box.
Here is my code
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
//IBOutlet UITextFields are here
override func viewDidLoad() {
super.viewDidLoad()
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.sizeToFit()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let fixedSpaceButton = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(self.doneClicked))
let nextButton = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(self.textFieldShouldReturn(_:)))
toolBar.setItems([fixedSpaceButton, nextButton, flexibleSpace, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
box1.delegate = self
box2.delegate = self
box3.delegate = self
box4.delegate = self
box5.delegate = self
box6.delegate = self
box1.becomeFirstResponder()
}
func doneClicked() {
view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
switch textField
{
case box1: box2.becomeFirstResponder()
break
case box2: box3.becomeFirstResponder()
break
case box3: box4.becomeFirstResponder()
break
case box4: box5.becomeFirstResponder()
break
case box5: box6.becomeFirstResponder()
break
default: textField.resignFirstResponder()
}
return true
}
//IBAction for maths calculations are here
I appreciate any help in advance. I literally have no hair left.
Thanks

QLPreviewController toolbar not appearing

I am using QLPreviewController to display a PDF. Now I am trying to add button items to the toolbar. When I print the toolbarItems I can see they are being added but my toolbar will not appear. Why is it not appearing?
func previewController(controller: QLPreviewController, previewItemAtIndex index: Int) -> QLPreviewItem {
let rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(drawCircle))
let rightButton2 = UIBarButtonItem(title: "Title2", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(drawCircle))
controller.setToolbarItems([rightButton, rightButton2], animated: false)
print(controller.toolbarItems)
let localUrl = String(format:"%#/%#", PDFFilePath, fileNameGroup)
let url = NSURL.fileURLWithPath(localUrl)
return url
}
viewPDF = QLPreviewController()
viewPDF.dataSource = self
self.presentViewController(viewPDF, animated: true, completion: nil)
From what I infered, I am not sure that you are adding the buttons to the right position. But the problem is for that you need to make a custom view controller and add QLPreviewController as a subview. Something like this:
class CustomQLViewController: UIViewController {
var customQL=QLPreviewController()
#IBOutlet weak var qlView: UIView! //defined in storyboard for QLPreviewController
override dynamic func viewDidLoad() {
super.viewDidLoad()
customQL.dataSource = self
customQL.view.frame = CGRectMake(0, 0, qlView.frame.size.width, qlView.frame.size.height)
qlView.addSubview(customQL.view)
}
What you see here is a view controller added in the storyboard with a custom UIView, used to represent the region that will be used up by QLPreviewController. Just add the buttons you want in the storyboard and connect their outlets in the class.
The main thing here is to set the QLPreview controller to the dimensions of the view you added in the storyboard (ie. qlView) and add set the frame of the QLPreview object to qlView. The final step would be to add the QLpreview as a subview in qlView.

UITapGestureRecognizer doesn't work

i want to set an UITapGestureRecognizer to permit to user to add an image as profile. I set the code but, when i touch on the image, nothing happens. Here the code:
#IBAction func selezionaFoto(sender: UITapGestureRecognizer) {
fieldNome.resignFirstResponder()
func selezionaLibreria(action : UIAlertAction!) {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.Default, animated: true)
CameraManager.sharedInstance.newImageFromLibraryForController(self, editing: false)
}
func scattaFoto(action : UIAlertAction!) {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.Default, animated: true)
var circle = UIImageView(frame: UIScreen.mainScreen().bounds)
circle.image = UIImage(named: "overlay")
CameraManager.sharedInstance.newImageShootForController(self, editing: false, overlay:circle)
}
var myActionSheet = UIAlertController(title: NSLocalizedString("ACTION_IMAGE_TITLE", comment: ""),
message: NSLocalizedString("ACTION_IMAGE_TEXT", comment: ""),
preferredStyle: UIAlertControllerStyle.ActionSheet)
myActionSheet.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_LIBRARY", comment: ""),
style: UIAlertActionStyle.Default,
handler: selezionaLibreria))
myActionSheet.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_SHOOT", comment: ""),
style: UIAlertActionStyle.Default,
handler: scattaFoto))
myActionSheet.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_CANCEL", comment: ""),
style: UIAlertActionStyle.Cancel,
handler: nil))
self.presentViewController(myActionSheet, animated: true, completion: nil)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func incomingImage(image: UIImage) {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
immagine.image = image
immagineSelezionata = image
}
func cancelImageSelection() {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
}
The first element in the storyboard is the image and then there is the textfield.
I recently faced the same issue, I had complicated view hierarchy and the reason why gesture didnt respond to the action, was that one superView was set to isUserInteractionEnabled to false, and as you may know, how gestures received by the app and go to the responder in the chain, is that all UIResponders in this chain should be isUserInteractionEnabled = true, to be delivered to the most top view. I assume that you faced the same situation, so just check all the views in the hierarchy.
I'm not 100% sure I understand your question, but it looks like you're just creating a UITapGestureRecognizer on your storyboard and connecting an action to it.
I think you mean to add a UITapGestureRecognizer to a UIImageView, which you can do like this:
var myImageview = UIImageView()
var myTapGestureRecognizer = UITapGestureRecognizer()
myImageview.addGestureRecognizer(myTapGestureRecognizer)
myTapGestureRecognizer.addTarget(self, action: Selector("methodName:"))
myImageview.userInteractionEnabled = true
func methodName(sender: UITapGestureRecognizer) {
// do stuff
}

I managed to segue to a blank view controller. How do I segue back?

//
// ViewController.swift
// FunFacts
//
// Created by Alex Macleod on 4/10/14.
// Copyright (c) 2014 Alex Macleod. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var funFactLabel: UILabel!
#IBOutlet weak var funFactButton: UIButton!
#IBOutlet weak var swipeView: UIView!
// let swipeRec = UISwipeGestureRecognizer()
let factBook = FactBook()
let colorWheel = ColorWheel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// swipeRec.addTarget(self, action: "swipedView")
// swipeView.addGestureRecognizer(swipeRec)
// swipeView.userInteractionEnabled = true
var swipeRight = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeRight.direction = UISwipeGestureRecognizerDirection.Right
self.view.addGestureRecognizer(swipeRight)
var swipeLeft = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeLeft.direction = UISwipeGestureRecognizerDirection.Left
self.view.addGestureRecognizer(swipeLeft)
var swipeDown = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeDown.direction = UISwipeGestureRecognizerDirection.Down
self.view.addGestureRecognizer(swipeDown)
var swipeUp = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeUp.direction = UISwipeGestureRecognizerDirection.Up
self.view.addGestureRecognizer(swipeUp)
funFactLabel.text = factBook.randomFact()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.Right:
// swipedAlertViewRight()
self.performSegueWithIdentifier("segueSwipeRight", sender: nil)
case UISwipeGestureRecognizerDirection.Left:
// swipedAlertViewLeft()
swipedLeft()
case UISwipeGestureRecognizerDirection.Down:
var randomColor = colorWheel.randomColor()
view.backgroundColor = randomColor
funFactButton.tintColor = randomColor
funFactLabel.text = factBook.randomFact()
case UISwipeGestureRecognizerDirection.Up:
var randomColor = colorWheel.randomColor()
view.backgroundColor = randomColor
funFactButton.tintColor = randomColor
funFactLabel.text = factBook.randomFact()
default:
break
}
}
}
func swipedLeft() {
self.performSegueWithIdentifier("segueSwipeLeft", sender: nil)
}
// func swipedAlertViewRight(){
// let tapAlert = UIAlertController(title: "Swiped", message: "You just swiped right", preferredStyle: UIAlertControllerStyle.Alert)
// tapAlert.addAction(UIAlertAction(title: "OK", style: .Destructive, handler: nil))
// self.presentViewController(tapAlert, animated: true, completion: nil)
// }
//
// func swipedAlertViewLeft(){
// let tapAlert = UIAlertController(title: "Swiped", message: "You just swiped left", preferredStyle: UIAlertControllerStyle.Alert)
// tapAlert.addAction(UIAlertAction(title: "OK", style: .Destructive, handler: nil))
// self.presentViewController(tapAlert, animated: true, completion: nil)
// }
#IBAction func showFunFact() {
var randomColor = colorWheel.randomColor()
view.backgroundColor = randomColor
funFactButton.tintColor = randomColor
funFactLabel.text = factBook.randomFact()
}
}
So I swipe left and I it takes me to a new viewViewcontroller, I swipe right it takes me to another blank view controller. How do I tell these blank view controllers to segue back to the main view controller?
Do you have a navigation bar in that view controller?
If you do, then you can simply do:
self.navigationController.popViewControllerAnimated(YES)
If you do not, then you simply need to
self.performSegueWithIdentifier("segueSwipeLeft", sender: nil)
(which is to say, perform a segue back to the view controller you came from). Segues are not necessarily a push and pop thing.
Segues create instances of their view controllers. This makes sense when moving in a forward direction, but if you "performSegueWithIdentifier" when moving in a backward direction, it's likely you're not returning to the previous view controller, but rather you're creating and presenting a new instance of the previous view controller.
For example, let's say you two view controllers, A and B, and A has a text field on it that has a value specified by the user. Then you segue to B. Then you use a standard segue back to A. The text won't be in the text field on A because you're looking at a new instance of A, not the original instance of that view controller.
If you want to back-up, there are Unwind segues, which are a special kind of segue to return you to a previous instance. They are rigged up to the green "exit" button at the top of your scene in the storyboard editor. Unwind segues (sometimes called Exit Segues) are interesting because they let you unwind not just to the previous view controller, but all the way back through a deep stack of view controllers, and as part of the unwind they can call different methods on the destination view controller, such as indicating that a Cancel or Save button was tapped on the source view controller.
Programatically, if your view controller was presented modally, you can also use dismissViewController:animated:completion: to back up.