NSNotification for all views at once - ios

I am trying to addObserver to all my views, when I start my application.
When there is a post coming I want to display a Modal View on top of the current ViewController.
Is there a way to install it directly on every View or do I need to do the
viewWillAppear : add
viewDidDisappear : remove
workaround each time ?

You could have created one superclass for all you view controllers and override viewWillAppear/viewDidDisappear there.
If there is no exception and you want to present a modal view controller no matter what view controller is currently on screen, you can present it over self.window.rootViewController in AppDelegate's didReceiveRemoteNotification method.

Create parent class like this and subclass all other classes
import UIKit
class TemplateClassVC: UIViewController {
override func viewWillAppear() {
}
override func viewDidDisappear() {
}
}
and find top viewcontroller like this
Get top most UIViewController

Related

Is it possible / smart to instantiate a view controller in a view controller?

I have a view controller that manages my entire view. Inside this view, I would like to create another four views which each have some button selector functions. I was wondering if it would be possible/smart for me to create a new View Controller class that has the functionality of these four sub-views and then instantiate these view controllers in the view controller that manages the full view?
Yes it is certainly possible. And it is often smart to do so. In your case it sounds like you may want to subclass UIView or UIControl though.
One of the main reasons to create a view controller is to keep track of the life cycle events of a view. In the view controller methods such as viewDidLoad() or viewDidAppear()
Creating view controllers works well for cases such as pages and segments, and navigation with UITabViewControllers and UINavigationControllers
You can generally just create view controller's and use them, something like this:
class MyViewController: UIViewController {
override func viewDidLoad() {
let subViewController = AnotherViewController()
view.addSubview(subViewController.view)
}
}
class AnotherViewController: UIViewController {
}

Is it possible to have a navigation controller handle gestures when it's embedded controller is shown without having delegate methods?

As mentioned I would like to know if it is possible to have an underlying navigationcontroller handle gestures when showing an embedded controller, without delegate methods.
I'm trying to make a slide out menu bar, and currently it's the embedded viewcontroller that handles the sliding out of the menu by a delegate method. I would love to make it so that the embedded viewcontroller has no idea that it's embedded in a slide out menu controller.
For the hierachy I have ContainerViewController, this is responsible for the whole thing. ContainerViewController has a MenuViewController (this handles the slide out menu) and a NavigationController with HomeController as rootViewController. HomeController is the viewcontroller I embed in the navigationcontroller.
I would like for the ContainerViewController to handle the UIEdgePanGesture even when the embedded HomeController is being shown. Currently HomeController has a delegate method that alerts the ContainerViewController that a UIEdgePanGesture has been made and thus opens the menu.
I tried applying the UIPanGestureRecognizer to the NavigationControllers view instead, but to no avail.
This is what I'd like to avoid, as there's really no reason to have every viewcontroller have responsibilities to the slideMenu, as they shouldn't know the slide menu exists in the firstplace.
protocol HomeControllerDelegate {
func handleMenuToggle()
func handleEdgePan(sender: UIScreenEdgePanGestureRecognizer)
}
class HomeController: UIViewController {
#objc func handleMenuToggle() {
//print("Menu was toggled...")
delegate?.handleMenuToggle()
}
#objc func handleEdgePan(sender: UIScreenEdgePanGestureRecognizer) {
//print("Menu was edgePanned...")
delegate?.handleEdgePan(sender: sender)
}
}

Refresh FirstView from SecondView in Tab Bar Controller

My app is using Tab Bar Controller which contains several View Controller in different tab. When user open the app, they will firstly enter FirstView. I would like to put some method in SecondView which refresh the FirstView. This is my FirstViewController.swift:
class FirstViewController: UIViewController, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
I have tried to put
FirstViewController().viewDidLoad()
in my SecondViewController.swift, but this is not working. Is there any better way to refresh the FirstView?
Try this way by make a static reference of firstViewController and then through this reference you can call any function
class ViewController: UIViewController {
static var firstVC : ViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("m on FirstViewController ")
ViewController.firstVC = self
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("m on SecondViewController ")
ViewController.firstVC?.viewDidLoad()
}
}
you can try this:-
instead of 0 pass the index of your FirstViewController
if let firstVC = self.tabBarController?.viewControllers?[0] as? FirstViewController {
firstVC.viewDidLoad()
}
You should not call viewDidLoad yourself. Its called only once when the view is loaded.
If you want to update the controllers view just before its displayed, you can use viewWillAppear for changing the layout or whatever you want to do.
The issue you're battling it how to tell another view controller that it needs to update its view. For this, you have two potential solutions because effectively, you're determining the best way to communicate between different objects.
Notifications are loosely decoupled and tend to be useful for one to many relationships. One object can fire off a notification and one or more objects can be listening for that notification. In your situation, a notification can be broadcast when a certain piece of state has changed in one view controller, and the other view controller can observe that notification so it can be notified when it should change.
Delegates are more closely coupled because they're one to one. They are often times implemented by creating a delegate property on an object that conforms to some protocol. Another object then assigns that delegate property to itself and implements the protocol so its implementation will be invoked whenever that function is called on the delegate. In your situation, each view controller could have a delegate property for some protocol(s). The tab bar controller can assign the delegate property to itself and handle the implementations of these functions. Therefore, whenever a change happens and a delegate is invoked, the tab bar controller can take can responsibility of telling which view controllers to update their view.
There are also of course other ways of handling your situation such as updating the view in viewWillAppear. This way, whenever a view controller appears on the screen, some code can execute that will update its view.
It ultimately depends on how you're storing application state and the design of your application.

viewWillAppear not firing for UIViewController that belongs to UITabBarController until I switch tabs

In my iOS app, I have a UITabBarController, and its viewControllers list looks like [vc1, vc2], which are of class MyVC1 and MyVC2 respectively, both of which subclass UIViewController. MyVC1 overrides viewWillAppear, and the overwritten version prints something so I know when it is called. (This is to isolate a larger problem I had, which was a view not appearing.)
My issue is that when the app first starts up and vc1 is the selected tab, nothing is printed, meaning its viewWillAppear is not being called. If I switch tabs and then go back to vc1, something does get printed, so vc1's viewWillAppear is not being called until I switch back to it from another tab.
Is there any way to have vc1 call viewWillAppear as soon as the app starts up, without needing to switch tabs? Honestly, I'm surprised this wasn't the default behavior already.
I just made a new test app in Xcode with nothing but a UITabViewController (and its two child view controllers) in Main.storyboard, and when I override viewWillAppear for the first child, my "view will appear" log prints every time (including on app launch).
Here are a couple things that could cause viewWillAppear to not be called:
Not calling super somewhere you've overridden viewWillAppear
Adding a view controller's view somewhere as a subview without adding the view controller as a child view controller
You also might try seeing if viewWillAppear is getting called for your UITabBarController, and if not, is it being called for its parent or presenting view controller? And so on until you find where the holdup is.
if you have Custom UITabBarController, check override func viewWillAppear is call super
in Custom UITabBarController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // this line need
}
Use the following line in viewWillAppear method of your vc1:
[super viewWillAppear:animated];
Next, the UITabBarController must be your initial controller.
make shure in the UITabBarController in the viewwillapear have the super.viewWillAppear(animated) like the following code
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}

Swift: Access Parent ViewController Segue from UIScrollView

What I have so far
I have a view controller ViewController1 which has 2 segues, one that goes to ViewController2 and one that comes back.
The segue from vc2 - vc1 is called "ViewC2toViewC1Segue"
On ViewController2 I have a UIScrollView that loads in two new viewcontrollers and allows me to scroll left or right to view them.
All these work fine, the data shows ok and everything displays nicely. However, On one of these subviews I want to be able to display an option to go back to ViewController1.
in my naivety I tried just using:
performSegueWithIdentifier("ViewC2toViewC1Segue", sender: self)
I hope this image helps explain:
The Problem
These two viewcontrollers that are loaded in the UIScrollView or not on the main storyboard so I can not CTRL and DRAG.
Question
How do I access the segue (ViewC2toViewC1Segue) of the view controller (ViewController2) that is holding the UIScrollView from one of UIScrollViews Child view containers.
I hope that makes sense.
The best way to do this is with a delegate protocol. Your parent view controller would be the delegate of the child. When the button is pushed on the child, it messages its delegate (the parent, who has the scroll view), and the parent handles scrolling to the other view controller.
In your ChildViewController file, you want to do 3 things:
Define the delegate protocol. This is a set of functions that the delegate object needs to implement.
Add a delegate property to the ChildVC class. This allows the ChildVC to call functions on the delegate.
Call the delegate function when the button is pressed
The protocol declaration would look something like this
protocol ChildViewControllerDelegate: class {
func childViewControllerDidSelectBack(childViewController: ChildViewController)
}
The delegate variable declaration would look like:
class ChildViewController: UIViewController {
weak var delegate: ChildViewControllerDelegate?
}
To call the delegate function, in your button handler code simply write:
delegate.childViewControllerDidSelectBack(self)
In your ParentViewController file, you want to do 3 things:
Set yourself as the delegate for the ChildVC
Declare that you conform to the ChildVCDelegate protocol
Implement the delegate protocol methods
To set yourself as the delegate, whenever you instantiate the child VC, do something like:
childVC.delegate = self
To declare that you conform to the protocol, make your class definition look like:
class ParentViewController: UIViewController, ChildViewControllerDelegate
Lastly, you need to implement the protocol function
func childViewControllerDidSelectBack(childViewController: ChildViewController){
// code to scroll the scrollview
}
}
Hopefully this helps!
You can use notification approach here.
On button click of one of your ViewController (in scrollview) post
notification.
Add observer in ViewController2's viewDidLoad method.
Go back to ViewController1 in selector method.
===================================================
//ViewController2
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(navigateToViewController1) name:#"navigateToViewController1" object:nil];
// Do any additional setup after loading the view from its nib.
}
-(void)navigateToViewController1 {
//[self.navigationController popViewControllerAnimated:YES];
OR
// perform segue
}
//In button click event of your viewcontroller inside scrollview.
[[NSNotificationCenter defaultCenter] postNotificationName:#"navigateToViewController1" object:nil];

Resources