When I click on a cell I want to push this view controller.
Here's my code:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let user = filteredUsers[indexPath.item]
print(user.username)
let userProfileController = UserProfileController(collectionViewLayout: UICollectionViewFlowLayout())
}
I want to push userProfileController.
Note: This is a UIView not a view controller
You can't push any controller from UIView. To do this you have to use NavigationController.
I'm assuming you have your UIView inside some UIViewController so one of the many options would be to create a delegate that will tell your view controller to do the push.
protocol MyViewDelegate {
func didTapButton()
}
class MyView: UIView {
weak var delegate: MyViewDelegate?
func buttonTapAction() {
delegate?.didTapButton()
}
}
class ViewController: UIViewController, MyViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
myView.delegate = self
}
func didTapButton() {
self.navigationController?.pushViewController(someVc, animated: true)
}
}
If your view controller in navigation controller stack and View controller holds view then you can follow
Block mechanism
func pushViewController(completion: () -> ()) {
}
Assign completion block from VC.
self.navigationController?.pushViewController(userProfileController, animated: true)
Try this
(superview?.next? as? UIViewController)?.navigationController?.pushViewController("your viewcontroller object", animated: false)
Related
I'm trying to set UIbutton's text in firstVC as selectedCity variable when it selected in tableViewCell in SecondVC . I cannot use segue or tabBarController. Updated photo of ui
FirstVC
import UIKit
class homeView: UIViewController{
#IBOutlet weak var selectRegion: UIButton!
}
SecondVC
import UIKit
class selectCityPage: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var selectedCity: String!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0{
selectedCity = "Almaty"
}
if indexPath.row == 1{
selectedCity = "Усть-Каменогорск"
}
dismiss(animated: true, completion: nil)
// when this view controller is dismissed, uibutton's text in next viewcontroller should equal to selectedCity
}
}
You can use delegation. These are the required steps:
Create a delegate protocol that defines the messages sent to the delegate.
Create a delegate property in the delegating class to keep track of the delegate.
Adopt and implement the delegate protocol in the delegate class.
Call the delegate from the delegating object.
SecondVC
import UIKit
//1. Create a delegate protocol
protocol CitySelectionDelegate {
func pickCity(with selectedCity:String)
}
class SelectCityPage: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var selectedCity: String!
//2. Create a delegate property in the delegating class
var delegate:CitySelectionDelegate?
//other stuff
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
if indexPath.row == 0{
selectedCity = "Almaty"
}
if indexPath.row == 1{
selectedCity = "Усть-Каменогорск"
}
4. Call the delegate from the delegating object.
delegate?.pickCity(with: selectedCity) //call your delegate method
//dismiss(animated: true, completion: nil) when you dismiss is up to you
}
}
HomeViewController
UPDATE: Since you have another VC embedded inside a UINavigationController, both the Home and Select Region Page MUST conform to the delegate and you have to set the delegate inside prepareForSegue method.
// 3. Adopt and implement the delegate protocol
class HomeViewController: UIViewController, CitySelectionDelegate{
#IBOutlet weak var selectRegion: UIButton!
func pickCity(with selectedCity: String) {
self.selectRegion.text = selectedCity
}
/*please pay attention. In this case you must reference the
navigation controller first and the your can get the right
instance of the delegate variable inside your firstVC (I called
firstVC but in your case is Select Region Page*/
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// you MUST set the right identifier
if segue.identifier == "showFirst" {
if let navController = segue.destination as? UINavigationController {
if let firstVC = navController.topViewController as? FirstViewController{
firstVC.delegate = self
}
}
}
}
}
since you have another VC (embedded in a navigation controller), also this one must conform to the delegate like so:
class FirstViewController: UIViewController, CitySelectionDelegate {
var delegate: CitySelectionDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
func pickCity(with selectedCity: String){
// here you simply call the delegate method again and you dismiss the navigation controller
self.delegate?.pickCity(with: selectedCity)
self.navigationController?.dismiss(animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecond" {
if let controller = segue.destination as? SelectCityPage {
controller.delegate = self
}
}
}
you can use delegate or block or notification when secVC action to change the firstVC value you needed.
Or you set a property in secVC and pass the value in firstVC you want to change, So you can change value passed from firstVC in secVC.
I'm new to IOS, creating a custom calendar where if the user selects any date, the app will redirect to another viewController.
I used this link to create a calendar:
https://github.com/Akhilendra/calenderAppiOS
I have done it with delegate but I can't figure out what I did wrong.
My code:
protocol SelectedDateDelagate: class {
func openAppointmentDetails()
}
class CalenderView: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, MonthViewDelegate {
weak var delegate: SelectedDateDelagate?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell=collectionView.cellForItem(at: indexPath)
cell?.backgroundColor=Colors.darkRed
let lbl = cell?.subviews[1] as! UILabel
lbl.textColor=UIColor.yellow
let calcDate = indexPath.row-firstWeekDayOfMonth+2
print("u selected date",calcDate,monthName)
delegate?.openAppointmentDetails()
}
}
class ViewController: UIViewController,SelectedDateDelagate {
func openAppointmentDetails() {
let myVC = storyboard?.instantiateViewController(withIdentifier: "appointmentDetailsVC") as! AppointmentDetailsViewController
navigationController?.pushViewController(myVC, animated: true)
}
}
now the problem is when I clicked on date nothing gonna happen.
It's better to use the storyboard in this case, and control-drag your collection view to the second view controller. Since you don't use your delegate for anything else other than presenting the view controller, you might as well just get rid of it.
Delegates are used in other cases. In this situation, it is not necessary. In addition, you do not need to conform UIView to Delegates and DataSource because it contradicts MVC. Instead, put the calendar view (collection view) inside the UIViewController and conform this controller to implement methods.
You should do this:
class CalenderViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var calenderView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)!
cell.backgroundColor = Colors.darkRed
let lbl = cell.subviews[1] as! UILabel
lbl.textColor = UIColor.yellow
let calcDate = indexPath.row - firstWeekDayOfMonth + 2
openAppointmentDetails()
}
func openAppointmentDetails() {
let appointmentVC = storyboard?.instantiateViewController(withIdentifier: "appointmentDetailsVC") as! AppointmentDetailsViewController
navigationController?.pushViewController(appointmentVC, animated: true)
}
}
I solved this. I forgot to add calenderView.delegate = self
in viewDidLoad() method of ViewController
and also changes in openAppointmentDetails()
here are the changes.
class ViewController: UIViewController,SelectedDateDelagate {
func openAppointmentDetails() {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "appointmentDetailsVC") as! AppointmentDetailsViewController
self.present(nextViewController, animated:true, completion:nil)
}
override func viewDidLoad() {
calenderView.delegate = self
}
}
Solved it: I forgot to declare tvx.selectionDelegate = self in VC1, thats why it never executed
I'v searched very long and fund two similar answers to my problem online:
here
But all of them work with dismissing and then presenting another view, and I want to dismiss and PUSH another ViewC.
I have 3 ViewController:
VC1 lets Call it: DownloadsViewController
VC2 lets Call it: SelectActionController
VC3 lets Call it: fileInfoView
VC1 presents VC2 modally then VC2 should dismiss and VC1 should PUSH VC3 immediately.
I've tried:
VC2:
self.present(vx, animated: true, completion: nil)
and the to put the PUSH animation in completion {} but it crashes.
The next thing I've tried is to make a delegate and if I press a button on VC2 it call VC1 to dismiss VC2 and Push VC3. Like Here:
VC2:
protocol PushViewControllerDelegate: class {
func pushViewController(_ controller: UIViewController)
}
class SelectActionController: UIViewController, UITableViewDelegate, UITableViewDataSource {
weak var delegate: PushViewControllerDelegate?
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if self.delegate != nil {
self.delegate?.pushViewController(self)}
VC1:
func pushViewController(_ controller: UIViewController) {
controller.dismiss(animated: true) { () -> Void in
self.performSegue(withIdentifier: self.segue2, sender: nil)
//let vx = self.storyboard?.instantiateViewController(withIdentifier: "FileInfo") as! fileInfoView
//self.navigationController?.pushViewController(vx, animated: false)
}
}
The Trird thing I tried was to make a global Variable and if it dismisses, just set a Bool variable to true and an func in VC1 should recognize it(my intention). Only problem it doesn't regognize dismissing as ViewDidAppear or stuff like that.
So none of that worked.
Does anyone has an Idea to this?
EDITED after OP comment
Just use a protocol.
Let your first controller adopt it, and add the required function.
Set a protocol variable in the second controller who takes the first one for reference and call the function when you dismiss the second controller.
You can now use the dismiss function to do whatever you want, pass data, send to an other controller...
first view controller :
protocol CustomProtocol {
func dismissed()
}
class AViewController: UIViewController, CustomProtocol {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func toVC2(_ sender: UIButton) {
let VC = self.storyboard!.instantiateViewController(withIdentifier: "BViewController") as! BViewController
VC.customProtocol = self
VC.view.backgroundColor = .white
self.present(VC, animated: true, completion: nil)
}
func dismissed() {
let yourViewController = UIViewController()
yourViewController.view.backgroundColor = .red
guard let navigationController = self.navigationController else {return}
navigationController.pushViewController(yourViewController, animated: true)
}
}
second view controller :
class BViewController: UIViewController {
var customProtocol: CustomProtocol?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func quit(_ sender: UIButton) {
self.dismiss(animated: true) {
guard let proto = self.customProtocol else {return}
proto.dismissed()
}
}
}
According to your File from Github, I think you forgot to declare
tvx.selectionDelegate = self
in the
func imagePressed
I hope you need to perform the dismiss action on your
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
in "SelectActionController" right ?
In such case you have to do as follows,
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if self.delegate != nil {
dismiss(animated: true) {
self.delegate?.pushViewController(self)}
}
}
I have shared an example project related to your situation, please refer it.
https://github.com/Bharath-MallowTech/StackoverflowSolutions/tree/master/viewcontrollerDismissAndPush
Could you post some code on what you've been trying? What is self here? Is it VC1, VC2 or VC3?
What you could do is have a look at protocols, so you use VC1 as a delegate and when you dismiss VC2 it will also call it's delegate showVC3(). You then place that delegate.showVC3() in the completion block for your dismiss.
// Declare it in VC1
#protocol MyDelegate {
func showVC3()
}
class VC1: UIViewController, MyDelegate {
var vc2: VC2ViewController = VC2ViewController()
var vc3: VC3ViewController = VC3ViewController()
func showVC2() {
vc2.delegate = self
self.present(self.vc2)
}
func showVC3() {
self.present(self.vc3)
}
}
// In VC2
class VC2: UIViewController {
var delegate: MyDelegate?
func closeVC2() {
self.dismiss(animated: true, completion: {
self.delegate?.showVC3()
})
}
}
Not sure if this works, haven't tried it out since I'm on my PC at the moment. But change it according to your needs and test it out.
Even i had the same situation , that after presenting a view controller , i need to push to some other view controller. If we have vc1, vc2, vc3, We presented vc2 from vc1, then on button click of vc2 we need to push to vc3, what i did is, i set vc3 as root view controller. You can check code as per below.
func goToHomeScreenPage(transition : Bool)
{
let _navigation : UINavigationController!
let startVC : TabbarViewController = Utilities.viewController(name: "TabbarViewController", onStoryboard: StoryboardName.Login.rawValue) as! TabbarViewController
_navigation = UINavigationController(rootViewController: startVC)
_navigation.setNavigationBarHidden(true, animated: true)
let transitionOption = transition ? UIViewAnimationOptions.transitionFlipFromLeft : UIViewAnimationOptions.transitionFlipFromLeft
gotoViewController(viewController: _navigation, transition: transitionOption)
}
the above method gotoviewcontroller() is below,
func gotoViewController(viewController: UIViewController, transition: UIViewAnimationOptions)
{
if transition != UIViewAnimationOptions.transitionCurlUp
{
UIView.transition(with: self.window!, duration: 0.5, options: transition, animations: { () -> Void in
self.window!.rootViewController = viewController
}, completion: { (finished: Bool) -> Void in
// do nothing
})
} else {
window!.rootViewController = viewController
}
}
Declare var window: UIWindow? on top, you can write these methods in appdelegate and call them by creating appdelegate instance.
I have read this thread (and similar others) from bottom to top, but it doesn't fit my needs at all.
I have a UIViewController inside UIPageViewController within a UINavigationController. Navigating to a 2nd ViewController. Navigating to a 3rd ViewController and want to pop back to 2nd ViewController delivering data.
My code currently:
protocol PassClubDelegate {
func passClub(passedClub: Club)
}
class My3rdVC: UIViewController {
var clubs: [Club] = []
var passClubDelegate: PassClubDelegate?
....
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let club = clubs[indexPath.row]
self.passClubDelegate?.passClub(club)
navigationController?.popViewControllerAnimated(true)
}
My 2nd VC:
class My2ndVC: UIViewController, PassClubDelegate {
var club = Club()
func passClub(passedClub: Club) {
SpeedLog.print("passClub called \(passedClub)")
club = passedClub
}
passClub is not called. I'm sure it's because I didn't set the delegate to the My2ndVC, but how would I do that? All the solutions I have found wanting me to use a) segue or b) instantiate a My2ndVC new, what doesn't make any sense since it's still in memory and I want to pop back to go back in hierarchy. What am I missing? What are my possibilities? Help is very appreciated.
PS: I'm not using any segues. My3rdVC is called by:
let vc = stb.instantiateViewControllerWithIdentifier("My3rdVC") as! My3rdVC
self.navigationController?.pushViewController(vc, animated: true)
You can set the delegate of My3rdVC in the prepareForSegue method of My2ndVC.
class My2ndVC: UIViewController, PassClubDelegate {
...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
switch segue.destinationController {
case let controller as My3rdVC:
controller.passClubDelegate = self
}
}
}
This is assuming you have created a segue in your storyboard that pushes My3rdVC from My2ndVC onto the navigation controller stack, which I'm assuming you have. So just try simply pasting this prepareForSegue method into My2ndVC and see if it works.
UPDATE
let vc = stb.instantiateViewControllerWithIdentifier("My3rdVC") as! My3rdVC
vc.passClubDelegate = self
navigationController?.pushViewController(vc, animated: true)
When pop one VC to another you can pass data using protocol by declare delegate variable as static. Here in the following example we pop SecondVC to FirstVC and we pass a string.
class FirstVC: UIViewController,getDataDelegateProtocol {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
SecondVC.delegate = self
}
func getData(tempStr: String) {
label.text = tempStr
}
#IBAction func buttonClick(_ sender: UIButton){
let nav = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondVC") as! SecondVC
self.navigationController?.pushViewController(nav, animated: true)
}
}
Another View Controller
protocol getDataDelegateProtocol{
func getData(tempStr: String)
}
class SecondVC: UIViewController {
#IBOutlet weak var label: UILabel!
static var delegate: getDataDelegateProtocol?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonClick(_ sender: UIButton){
SecondVC.delegate?.getData(tempStr: "Received data from SecondVC")
self.navigationController?.popViewController(animated: true)
}
}
i'm trying to start writing Swift and i'm trying to get a value from a modal view controller with no luck.
I have two controllers, the ViewController and modalViewController.
In ViewController i have a UITableView and with a press of a button i open the modalViewController.
Then from a UITextField i pass the value.
I have implement a protocol with delegate and func but somewhere i'm missing something or had it wrong.
ViewController.swift
import UIKit
class ViewController: UIViewController,UITableViewDelegate,modalViewControllerDelegate{
#IBOutlet var table: UITableView!
var tableData = ["First Row","Second Row","Third Row"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
table.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(table:UITableView?,numberOfRowsInSection section: Int) -> Int
{
return tableData.count
}
func tableView(table:UITableView?,cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default,reuseIdentifier:"cell")
cell.textLabel?.text = tableData[indexPath.row]
return cell
}
func sendText(text: NSString) {
tableData.append(text)
} }
modalViewController.swift
import UIKit
protocol modalViewControllerDelegate {
func sendText(var text: NSString)
}
class modalViewController: UIViewController{
let delegate: modalViewControllerDelegate?
#IBOutlet var textField: UITextField?
#IBAction func cancelButton(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func saveButton(sender: AnyObject) {
delegate?.sendText(self.textField!.text)
dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
}
I have no errors in the code, the delegate is not working, it's always nil.
Thanks.
You have to assign the delegate in your first view controller.
Also, you have to change let delegate: modalViewControllerDelegate? to a var, or else you can't change it.
Right now your delegate is empty.
It's unclear how you're accessing ModalViewController. If you're using segues:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "modalViewControllerSegue" {
var destination = segue.destinationViewController as CategoryViewController
destination.delegate = self
}
}
Or if you're doing it programmatically:
var modalViewController = ModalViewController(parameters)
modalViewController.delegate = self
presentViewController(modalViewController, animated: true, completion: nil)
Storyboard identifier:
let destination = UIStoryboard.mainStoryboard().instantiateViewControllerWithIdentifier("ModalViewController") as ModalViewController
delegate = self
showViewController(destination, sender: nil)
EDIT:
If you want to access ModalViewController by selecting a cell you need the tableView: didSelectRowAtIndexPath method:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("modalViewControllerSegue", sender: self)
}
Using this, you'll need the method prepareForSegue to set the delegate.
You have to set your modalViewController's delegate property before presenting it. If you're using segues, you can do this in prepareForSegue(_:).
Also, class names should begin with uppercase letters (modalViewController should be ModalViewController). Only instances should begin with lowercase letters.
Another option, instead of using delegation, is to use an unwind segue. Here's a tutorial: http://www.cocoanetics.com/2014/04/unwinding/
In your case, in your presenting view controller you could have the method:
func returnFromModalView(segue: UIStoryboardSegue) {
// This is called when returning from the modal view controller.
if let modalVC = segue.sourceViewController as? ModalViewController
where segue.identifier == "mySegueID" {
let text = modalVC.textField.text
// Now do stuff with the text.
}
}
And then just link up everything in the Interface Builder as shown in the tutorial.