I have some methods in second view controller. I access them through delegate - inside func buttonSender. Main goal is to pass currentTitle of a button as an argument to a viewController.addNewMessage. What is the best way to do this?
ChatViewController:
protocol ReactToButtons {
func buttonSender(viewController: MyChatViewController)
}
class MyChatViewController: ChatViewController{
var delegate: ReactToButtons?
override func viewDidLoad() {
super.viewDidLoad()
delegate!.buttonSender(self)
}
}
ContainerViewController:
class ContainerViewController: UIViewController, ReactToButtons {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let myVC = segue.destinationViewController as! MyChatViewController
myVC.delegate = self
}
func buttonSender(viewController: MyChatViewController) {
viewController.addNewMessage(/*HERE GOES CURRENT TITLE*/)
}
#IBAction func leftButtonPressed(sender: AnyObject) {
let currentTitle = sender.currentTitle!
}
I have change my answer to suit your code
class ContainerViewController: UIViewController, ReactToButtons {
var delegate: MyChatViewController!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let myVC = segue.destinationViewController as! MyChatViewController
myVC.delegate = self
}
func buttonSender(viewController: MyChatViewController) {
delegate = viewController
}
#IBAction func leftButtonPressed(sender: AnyObject) {
let currentTitle = sender.currentTitle!
delegate!.addNewMessage(currentTitle)
}
}
Related
I am new to Xcode and am trying to save an array from secondViewController into the View controller. I have a a series of view controllers embedded in a navigation controller so when I click 'back' on the navigation bar I want to keep the data that was collected in an array 'collectionArray' and save to 'collectionArray2' . Here is the protocol delegate method I've tried:
This is in my ViewController where I want the array saved to :
import UIKit
class ViewController: UIViewController {
var collectionArray2: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
let controller = secondViewController()
controller.delegate = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let first = segue.destination as! secondViewController
first.collectionArray.append(contentsOf: collectionArray2)
}
}
extension ViewController: PopupDelegate {
func popupSelectedView(array: [String]) {
collectionArray2.append(contentsOf: array)
}
}
This is my secondViewController where I want to take 'collectionArray':
import UIKit
protocol PopupDelegate: class{
func popupSelectedView(array: [String])
}
class secondViewController: UIViewController {
var exerciseButton: String!
var collectionArray: [String] = []
weak var delegate: PopupDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Exercisess(_ sender: UIButton){
exerciseButton = sender.currentTitle
collectionArray.append(exerciseButton!)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
delegate?.popupSelectedView(array: collectionArray)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let second = segue.destination as! FinalViewController
second.cellLabelArray.append(contentsOf: collectionArray)
}
}
Thank you
your problem is that your are referring to a difference instance of secondViewController. Don't set your delegate in viewDidLoad but when you prepare the segue:
override func viewDidLoad() {
super.viewDidLoad()
//let controller = secondViewController() REMOVE THIS
//controller.delegate = self REMOVE THIS
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let first = segue.destination as! secondViewController
first.collectionArray.append(contentsOf: collectionArray2)
first.delegate = self // set the delate here
}
}
by the way, you should name all your classes starting with capital letters, so it should be SecondViewController
I am using a modal segue to create a view controller that I want to dismiss and then call a method in the origin view controller. I have used a protocol/delegate pattern but for some reason the method is never called. The code is below: Please note that I removed a lot of non-relevant code to keep it clean here
Thanks!!
VC1:
final class WorkoutViewController: UIViewController, StartWorkoutButtonDelegate {
weak var dataSource: WorkoutViewControllerDataSource!
private var workout: Workout!
private var workoutDataViewController: WorkoutDataViewController?
private var workoutFinishViewController: WorkoutFinishViewController?
private let workoutFinishViewControllerSegueIdentifier = "Workout Finish View Controller Segue"
#IBOutlet private var primaryActionButton: UIButton!
#IBOutlet private var pageControl: UIPageControl!
// MARK: - Handling View Lifecycle
override func viewDidLoad() {
super .viewDidLoad()
let workoutType = dataSource.workoutType(for: self)
workout = Workout(workoutType: workoutType, managedObjectContext: persistentContainer.viewContext)
setPrimaryActionButtonLabel()
}
// MARK: - Handling Storyboard Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let destinationWorkoutFinishViewController = segue.destination as? WorkoutFinishViewController {
workoutFinishViewController = destinationWorkoutFinishViewController
}
}
// MARK: - Managing Start and Pause
#IBAction func startOrPauseWorkout() {
if workout.isPaused {
startWorkout()
} else {
pauseWorkout()
}
}
}
VC 2:
protocol StartWorkoutButtonDelegate: AnyObject {
func startOrPauseWorkout()
}
class WorkoutFinishViewController: UIViewController {
weak var delegate: StartWorkoutButtonDelegate?
#IBAction func startWorkout() {
self.delegate?.startOrPauseWorkout()
self.dismiss(animated: true, completion: nil)
}
#IBAction func finishWorkout() {
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Here set the delegate
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let destinationWorkoutFinishViewController = segue.destination as? WorkoutFinishViewController {
destinationWorkoutFinishViewController.delegate = self
}
}
I have a table view controller that works like a news feed of images where each image has been retrieved from firebase. I want to be able to tap on each image and perform a segue to another view controller that has a table view with its own unique data of the tapped image.
With the code I have, the override func prepare(for:sender) is not being called and there is no error. The unique post id is being printed in the console when the image is tapped but the Show Segue is not being performed.
Here is my code in the HomeTableViewCell:
protocol HomeTableViewCellDelegate {
func goToSiteVC(postId: String)
}
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var siteImageView: UIImageView!
var delegate: HomeTableViewCellDelegate?
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.siteImageView_TouchUpInside))
siteImageView.addGestureRecognizer(tapGesture)
siteImageView.isUserInteractionEnabled = true
// Configure the view for the selected state
}
#objc func siteImageView_TouchUpInside() {
print("siteImageView_TouchUpInside")
print(post?.id)
if let id = post?.id {
delegate?.goToSiteVC(postId: id)
}
}
}
Here is my code in HomeViewController:
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
loadPosts()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SiteSegue" {
let siteVC = segue.destination as! SiteViewController
let postId = sender as! String
siteVC.postId = postId
print("segue")
}
}
}
extension HomeViewController: HomeTableViewCellDelegate {
func goToSiteVC(postId: String) {
performSegue(withIdentifier: "SiteSegue", sender: postId)
}
}
The function should be out of viewDidLoad it must be in class scope
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
loadPosts()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SiteSegue" {
let siteVC = segue.destination as! SiteViewController
let postId = sender as! String
siteVC.postId = postId
print("segue")
}
}
}
My code is below. When I press the 'Done' or 'Cancel' buttons, it doesn't work as I want. I did some debugging, and delegate is nil even though I set it. Please help - thanks.
class ViewController: UIViewController,EditViewControllerDelegate {
#IBOutlet weak var label: UILabel!
//页面跳转时
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditView" {
//通过seque的标识获得跳转目标
let controller = segue.destinationViewController as! EditViewController
//设置代理
controller.delegate = self
//将值传递给新页面
controller.oldInfo = label.text
}
}
func editInfo(controller:EditViewController, newInfo:String){
label.text = newInfo
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
func editInfoDidCancer(controller:EditViewController){
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
import UIKit
protocol EditViewControllerDelegate {
func editInfo(controller:EditViewController, newInfo:String)
func editInfoDidCancer(controller:EditViewController)
}
class EditViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate:EditViewControllerDelegate?
var oldInfo:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if oldInfo != nil{
textField.text = oldInfo
}
}
#IBAction func done(sender: AnyObject) {
delegate?.editInfo(self, newInfo: textField.text!)
}
#IBAction func cancel(sender: AnyObject) {
delegate?.editInfoDidCancer(self)
}
}
Try this,
class ViewController: UIViewController,EditViewControllerDelegate {
var controller: EditViewController?
#IBOutlet weak var label: UILabel!
//页面跳转时
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditView" {
//通过seque的标识获得跳转目标
controller = segue.destinationViewController as! EditViewController
//设置代理
controller.delegate = self
//将值传递给新页面
controller.oldInfo = label.text
}
}
func editInfo(controller:EditViewController, newInfo:String){
label.text = newInfo
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
func editInfoDidCancer(controller:EditViewController){
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
import UIKit
protocol EditViewControllerDelegate {
func editInfo(controller:EditViewController, newInfo:String)
func editInfoDidCancer(controller:EditViewController)
}
class EditViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate:EditViewControllerDelegate?
var oldInfo:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if oldInfo != nil{
textField.text = oldInfo
}
}
#IBAction func done(sender: AnyObject) {
delegate?.editInfo(self, newInfo: textField.text!)
}
#IBAction func cancel(sender: AnyObject) {
delegate?.editInfoDidCancer(self)
}
}
I’m not able to tell from your code how you are handling the opening of EditViewController from your ViewController.
I’m guessing that your prepareForSegue:sender: is not getting called resulting in the delegate not getting set. To fix this, you will need to add a performSegueWithIdentifier:sender: call at the point where the segue needs to happen as in
self.performSegueWithIdentifier("EditView", sender: self)
You should replace whatever code is performing the opening of EditViewController with that call.
I have an app the uses showViewController:sender: to open up a second view controller from the first one even though there is a Show segue defined in my storyboard. The segue in my storyboard doesn’t get used in that case and prepareForSegue:sender: is never called as a result.
If I replace my
showViewController(myVC, sender: self)
with
performSegueWithIdentifier("mySegue", sender: self)
then prepareForSegue:sender: will get called. If I am setting a delegate, like you are, in prepareForSegue:sender:, the delegate will get set before the segue happens.
I have a screen with a text field.
When a user inputs text into that text field, and taps out of the text field, a segue occurs.
The following screen's label text should be changed to the text entered by the user in the previous.
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var inputField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
inputField.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
inputField.resignFirstResponder()
}
func textFieldDidEndEditing(inputField: UITextField) {
let info = inputField.text
print(info)
performSegueWithIdentifier("goToBlue", sender: info)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "goToBlue" {
if let blueVC = segue.destinationViewController as? BlueViewController {
if let sentValue = sender as? String {
blueVC.receptacle = sentValue
}
}
}
}
}
The print(info) statement does not print anything
However, the segue works, but the label displays nothing.
class BlueViewController: UIViewController {
#IBOutlet weak var blueText: UILabel!
var receptacle = ""
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
blueText.text = receptacle
}
}
Clearly this has to do with timing... any ideas?
Your viewWillAppear function works first and that time your receptacle variable is not set or your label does not initialized. Try this;
#IBOutlet weak var blueText: UILabel! { didSet { blueText.text = receptacle } }
var receptacle: String? { didSet { blueText.text = receptacle } }
Change
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
blueText.text = receptacle
}
to
override func viewDidLoad() {
super.viewDidLoad()
blueText.text = receptacle
}
Don't use the sender to pass your value. The sender is only useful to check which element has started the segue, but not to pass data. Just make your info as a class-variable and pass the value like that:
var info:String!
func textFieldDidEndEditing(inputField: UITextField) {
info = inputField.text
print(info)
performSegueWithIdentifier("goToBlue", sender: info)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "goToBlue" {
if let blueVC = segue.destinationViewController as? BlueViewController {
blueVC.receptacle = info
}
}
}
Here is the very common way to do it (FYI, if the vc with inputTextfield is a popover, you can just call dismissViewController on the presenting vc)
func textFieldDidEndEditing(inputField: UITextField) {
textField.resignFirstResponder()
let info = inputField.text
print(info)
performSegueWithIdentifier("goToBlue", sender: info)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "goToBlue" {
if let blueVC = segue.destinationViewController as? BlueViewController {
blueVC.receptacle = self.inputField.text
}
}
}