I have 2 view controller.
1. ViewController (VC)
2. WebViewController (WVC)
In VC I click on a button and WVC is shown.
After successfully completing all tasks in WVC, I want to show VC and execute a particular function in VC.
Now, Storyboard contains some objects and add to VC using click and drag.
Initially, it has some values(not nil).
I Click on button, WVC is pushed, After completing all task in WVC,
VC is pushed.
Now some values in VC is nil.
error,welcome (UILabel, UIView) is nil...
And also is want to call a function in VC.
How it is possible.?
Code
ViewController.swift
class LoginController:UIViewController,UITextFieldDelegate{
#IBOutlet weak var password: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var error: UILabel!
#IBOutlet weak var welocome: UILabel!
......
#IBAction func BtnClicked(sender: AnyObject) {
let webViewCnt = self.storyboard!.instantiateViewControllerWithIdentifier("WebViewController") as UIViewController
self.navigationController?.pushViewController(webViewCnt, animated: true)
}
func doSomeProcess(){
}
}
WebViewController.swift
class WebViewController: UIViewController, UITextFieldDelegate,UIWebViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
self.start()
}
func start(){
---- do some process ----
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let ViewCont = self.storyboard!.instantiateViewControllerWithIdentifier("LoginController") as UIViewController
self.navigationController?.pushViewController(ViewCont, animated: true)
ViewController().doSomeProcess() // call viewcontroller doSomeProcess() function
}
})
}
}
Simple your are pushing VC to WVC,
So logically after pushing WVC to NavigationController.
it added WVC to the top of Navigation Stack.
& it it better to pop this WVC from Navigation will automatically show VC with previous values.
use this
ViewController.swift
class LoginController:UIViewController,UITextFieldDelegate{
#IBOutlet weak var password: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var error: UILabel!
#IBOutlet weak var welocome: UILabel!
var isBackFromWVC = false
......
#IBAction func BtnClicked(sender: AnyObject) {
let webViewCnt = self.storyboard!.instantiateViewControllerWithIdentifier("WebViewController") as UIViewController
self.navigationController?.pushViewController(webViewCnt, animated: true)
isBackFromWVC = true
}
func doSomeProcess(){
}
func vieWillAppear(){
if(isBackFromWVC){
doSomeProcess()
}
}
}
WebViewController.swift
class WebViewController: UIViewController, UITextFieldDelegate,UIWebViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
self.start()
}
func start(){
---- do some process ----
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationController?.popViewController(animated:true)
}
})
}
}
Try to pop:
In this start() method just replace a line self.navigationController?.popViewController(animated: true)
instead of pushing the ViewController.
func start(){
---- do some process ----
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let ViewCont = self.storyboard!.instantiateViewControllerWithIdentifier("LoginController") as UIViewController
self.navigationController?.pushViewController(ViewCont, animated: true)
ViewController().doSomeProcess() // call viewcontroller doSomeProcess() function
}
})
}
Reason:
When you are pushing to any ViewController, The storyboard will create a new reference of ViewController and the OOP's says every object has its own properties and method so that's why your pushing view controller properties may be nil assume that your username: UITextField! text will be nil due to separate instance of ViewController.
For calling the function of your view-controller you have to implement the delegate if it is not for every time else you may work in viewwillappear.
From my understanding, probably what you want is to dismiss WVC and go back to VC.
in WebViewController's start():
self.navigationController?.popViewController(animated: true)
and after pop, you need to execute doSomeProcess() function. There're several ways to do this and I'll introduce one here.
in ViewController, implement UINavigationControllerDelegate:
func navigationController(_ navigationController: UINavigationController, animationCOntrollerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewCOntroller) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .pop:
let to = toVC as! ViewController
to.doSomeProcess()
default:
break
}
return nil
}
of course you'll need
self.navigationController?.delegate = self
inside VC's viewDidLoad() as well.
Related
class FourthViewController : UIViewController {
#IBOutlet weak var previousLabel: UILabel!
#IBOutlet weak var backButton: UIButton!
var delegate: FourthToFirst?
var label = ""
// MARK: - Lifecycle method
override func viewDidLoad() {
super.viewDidLoad()
previousLabel.text = label
let fourthViewController = storyboard?.instantiateViewController(withIdentifier: "FourthViewController") as? FourthViewController
navigationController?.pushViewController(fourthViewController!, animated: true)
}
// MARK: - IBAction
#IBAction func backToFirst(_ sender: Any) {
navigationController?.popToRootViewController(animated: true)
}
Actually I am in fourthviewController it was going recursively(i.e pushing the fourthViewcontroller again and again, nonstop)
if I pressed back button in the controller I have to go back(i.e firstviewcontroller)
problem is:
In my code it was going (i.e non stop)
I can't press the back button to go back(i.e firstviewcontroller)
navigationController?.popViewController(animated: true) instead of navigationController?.popToRootViewController(animated: true) could be what's you need mate. (Based on what's you just edited)
I have a main view controller, a container view and 2 child view controllers and i would like to be able to switch between the children (for example: when the application loads for the first time, i would like that the controller containing the MapView to be loaded and when i press the Search Bar found in the main view, the controller with the table to be loaded).
Here is my storyboard: https://i.stack.imgur.com/rDPMe.png
MainScreen.swift
class MainScreen: UIViewController {
#IBOutlet private weak var searchBar: UISearchBar!
#IBOutlet private weak var ContainerView: UIView!
//private var openSearchBar: Bool?
private var openMapView: Bool = true
private var openPlacesList: Bool = false
private var containerView: ContainerViewController!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let containerView = segue.destination as? ContainerViewController
if containerView == nil{
containerView = segue.destination as? ContainerViewController
}
if openMapView == true{
containerView!.moveToMapView()
}
else if openPlacesList == true{
containerView!.MoveToOpenPlaces()
}
}
}
//search bar delegate functions
extension MainScreen: UISearchBarDelegate{
//detects when text is entered
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
openPlacesList = true
openMapView = false
containerView!.MoveToOpenPlaces()
}
}
ContainerViewController.swift:
class ContainerViewController: UIViewController {
private var childViewController: UIViewController!
private var first: UIViewController?
private var sec: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MainToMap"{
first = segue.destination as! MapViewController
self.addChild(first!)
self.view.addSubview(first!.view)
self.didMove(toParent: self)
}else{
sec = segue.destination as! PlacesListController
}
if(first != nil && sec != nil){
interchange(first!,sec!)
}
}
func interchange(_ oldVc: UIViewController,_ newVc: UIViewController ){
oldVc.willMove(toParent: nil)
self.addChild(newVc)
self.view.addSubview(newVc.view)
self.transition(from: oldVc, to: newVc, duration: 2, options: UIView.AnimationOptions.transitionCrossDissolve, animations: {
newVc.view.alpha = 1
oldVc.view.alpha = 0
}, completion: { (complete) in
oldVc.view.removeFromSuperview()
oldVc.removeFromParent()
newVc.willMove(toParent: self)
})
}
func moveToMapView(){
performSegue(withIdentifier: "MainToMap", sender: nil)
}
func MoveToOpenPlaces(){
performSegue(withIdentifier: "MainToSearches", sender: nil)
}
}
The problem is that when I press the search bar, it calls the method interchange and then it just gives a SIGABRT 1 error. I tried this tutorial: https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1 and many more but so far no luck. I am stucked here and don't know how i can solve this problem.
Stack: https://i.stack.imgur.com/Zqpm1.png
SIGABR 1 Error: https://i.stack.imgur.com/NBgEN.png
You appear to be trying to manually transition between child view controllers, but at the same time using segues (which do their own transitioning for you). Eliminate the segues (other than the initial embed segue, if you're using a storyboard with a "container view"), and just manually instantiate the child view controllers using their storyboard IDs. But don't use segues and then try to replace the child view controllers in prepare(for:sender:).
Also, when you use transition(from:to:duration:options:animations:completion:), you should not add the views the the view hierarchy yourself. That method does that for you (unless you use the showHideTransitionViews option, which tells the method that you're taking this over, something we don't need to do here). Likewise, when you use the transitionCrossDissolve option, you don't need to mess with alphas, either.
Thus, using the code snippet from that article you reference, you can do:
class FirstViewController: UIViewController {
#IBOutlet weak var containerView: UIView! // the view for the storyboard's "container view"
#IBOutlet weak var redButton: UIButton! // a button to transition to the "red" child view controller
#IBOutlet weak var blueButton: UIButton! // a button to transition to the "blue" child view controller
// tapped on "transition to red child view controller" button
#IBAction func didTapRedButton(_ sender: UIButton) {
redButton.isEnabled = false
blueButton.isEnabled = true
let oldVC = children.first!
let newVC = storyboard!.instantiateViewController(withIdentifier: "RedStoryboardID")
cycle(from: oldVC, to: newVC)
}
// tapped on "transition to blue child view controller" button
#IBAction func didTapBlueButton(_ sender: UIButton) {
blueButton.isEnabled = false
redButton.isEnabled = true
let oldVC = children.first!
let newVC = storyboard!.instantiateViewController(withIdentifier: "BlueStoryboardID")
cycle(from: oldVC, to: newVC)
}
func cycle(from oldVC: UIViewController, to newVC: UIViewController) {
// Prepare the two view controllers for the change.
oldVC.willMove(toParent: nil)
addChild(newVC)
// Get the final frame of the new view controller.
newVC.view.frame = containerView.bounds
// Queue up the transition animation.
transition(from: oldVC, to: newVC, duration: 0.25, options: .transitionCrossDissolve, animations: {
// this is intentionally blank; transitionCrossDissolve will do the work for us
}, completion: { finished in
oldVC.removeFromParent()
newVC.didMove(toParent: self)
})
}
func display(_ child: UIViewController) {
addChild(child)
child.view.frame = containerView.bounds
containerView.addSubview(child.view)
child.didMove(toParent: self)
}
func hide(_ child: UIViewController) {
child.willMove(toParent: nil)
child.view.removeFromSuperview()
child.removeFromParent()
}
}
That yields:
So I'm having a a VC which is embedded in a navigation controller which in turn is embed in a Parent VC with 2 more containers. For all three containers I'm using function (from Parent VC) to manipulate them to show them.
As can be seen in the picture when I'm click on the 3 lines button from Voice VC it crashing with Could not cast value of type 'UINavigationController' (0x1b5614200) to 'Pro.VoiceParentViewController
If i embed just the Voice Parent VC ..my back buttons dont work anymore _ = navigationController?.popToRootViewController(animated: true)
cause
po navigationController -> nil
EDIT
class VoiceParentViewController: UIViewController {
#IBOutlet weak var container_voiceVC: UIView!
#IBOutlet weak var container_menuVC: UIView!
#IBOutlet weak var containerSettingsVC: UIView!
#IBOutlet weak var constraint_trailingVoiceContainer: NSLayoutConstraint!
#IBOutlet weak var constraint_leadingVoiceContainer: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
}
func menuTouched() {
constraint_leadingVoiceContainer.constant = self.view.bounds.maxX
constraint_trailingVoiceContainer.constant = 2*self.view.bounds.maxX
UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: .curveEaseInOut, animations: {
self.view.layoutIfNeeded()
}) { (isCompleted) in
}
}
.....
class VoiceViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
#IBAction func action_menu(_ sender: Any) {
(self.parent as! VoiceParentViewController).menuTouched()
}
........
class NewConversationViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var mainTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
#IBAction func backButtonPressed(_ sender: Any) {
_ = navigationController?.popToRootViewController(animated: true)
}
Embedded your ProfileViewController and VoiceViewController in navigationcontroller.
Edited
I think problem is using child controllers with container view. I might be wrong. But i have created a quick demo for you same as your navigation cycle. Checkout this code - https://drive.google.com/open?id=1faFagqDZwpfRN7lzDPknQAeWGvhx3271
Just embedded your VoiceParentViewController in navigation controller instead of embedded VoiceViewController in navigation controller
Embed VoiceParentViewController in the NavigationController then make sure the first ViewController is VoiceParentViewController and then this pushes VoiceViewController. This would make sure navigationController?.popToRootViewController(animated: true) works.
I have been searching for how the delegate works and I tried to do it in my project. Unfortunately, the delegate method I implement does not get called ever. I am trying to do a slide-out navigation panel. so what I did is that I put two uicontainerviews, one is for slide-out navigation panel and the other for main view controller
enter image description here
The code is that
For main view controller
protocol MainViewControllerDelegate {
func toggleSideMenu()
}
class MainViewController: UIViewController {
var delegate: MainViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Slide Action
#IBAction func slideMenuTapped(_ sender: UIBarButtonItem){
delegate?.toggleSideMenu()
print("Slide Menu has been tapped")
}
}
For container view controller
class ContainerVC: UIViewController {
#IBOutlet weak var SideMenuConstraint: NSLayoutConstraint!
#IBOutlet weak var slideMenuContainer: UIView!
#IBOutlet weak var mainViewContainer: UIView!
var mainViewController: MainViewController?
var isSideMenuOpened = false
override func viewDidLoad() {
super.viewDidLoad()
mainViewController = UIStoryboard.mainViewController()
mainViewController?.delegate = self
}
}
extension ContainerVC: MainViewControllerDelegate{
func toggleSideMenu() {
print("It works")
if isSideMenuOpened{
isSideMenuOpened = false
SideMenuConstraint.constant = -260
mainViewContainer.layer.shadowOpacity = 0
} else {
isSideMenuOpened = true
SideMenuConstraint.constant = 0
mainViewContainer.layer.shadowOpacity = 0.59
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
}
extension UIStoryboard{
static func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: Bundle.main) }
static func mainViewController() -> MainViewController? {
return mainStoryboard().instantiateViewController(withIdentifier: "MainViewController") as? MainViewController
}
}
Please let know what's wrong
I think the reason is that you embed your main view controller in navigation controller :
let navigationController = self.childViewControllers.last as! UINavigationController
let mainViewController = navigationController.topViewController as! MainViewController
mainViewController?.delegate = self
Here is where you got wrong:
mainViewController = UIStoryboard.mainViewController()
mainViewController?.delegate = self
this mainViewController is not the same as the child of the container view controller, so setting its delegate doesn't really do anything.
You need to first get the VC that is the child of the container view controller:
mainViewController = self.childViewControllers.last as! MainViewController
mainViewController.delegate = self
I have a UIPageViewcontroller containing three ViewControllers. One Viewcontroller is my ProfileViewcontroller. I have a button in my ProfileViewController, which should tell the UIPageViewCongtroller when pressed, to switch to the next Viewcontroller.
It's my first time implementing a delegate, but I just can't figure out why its not working.
UIPageViewController class:
class PageViewController: UIPageViewController, ProfileViewControllerDelegate {
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
self.newColoredViewController("Profile"),
self.newColoredViewController("Offer")]
}()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if orderedViewControllers.count >= 2 {
scrollToViewController(orderedViewControllers[1])
}
}
// MARK: ProfileViewControllerDelegate
func profileViewControllerDidTouchOffer(viewController:ProfileViewController, sender: AnyObject) {
scrollToNextViewController()
print("I'm pressing the offer Button")
}
ProfileViewController class:
protocol ProfileViewControllerDelegate : class {
func profileViewControllerDidTouchOffer(controller: ProfileViewController, sender: AnyObject)
}
class ProfileViewController: UIViewController {
weak var delegate: ProfileViewControllerDelegate?
#IBOutlet var profileImageView: SpringImageView!
#IBOutlet var offerButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func offerButtonTouchUpInside(sender: AnyObject) {
delegate?.profileViewControllerDidTouchOffer(self, sender: sender)
}
Answer
I updated the PageViewController class by changing the way I add the ViewControllers in orderderedViewControllers:
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
let profileVC = UIStoryboard(name: "Main", bundle: nil) .instantiateViewControllerWithIdentifier("ProfileViewController") as! ProfileViewController
profileVC.delegate = self
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
profileVC,
self.newColoredViewController("Offer")]
}()
Looks like you're using the InterfaceBuilder so I'm not sure how it's done there (probably in prepareForSegue), but a typical pattern is something like this:
let vc = ProfileViewController()
vc.delegate = self // Or whatever you want to act as the delegate
// Now show the view controller.
The key is that before using the view controller, you make sure it's delegate property is set to the thing that is the delegate and conforms to the protocol. Typically the calling controller would be the delegate.
EDIT: If you're using the UIPageViewController, then you should add the delegate to the viewController before passing it to setViewControllers https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/#//apple_ref/occ/instm/UIPageViewController/setViewControllers:direction:animated:completion: