I want to change button image whenever UISideMenuNavigationController Appear Or Disappear.
This is the class that has a button.
class MenuViewController: UIViewController {
#IBOutlet var btnMenu: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
}
This is the other class that i want to insert code.
open class UISideMenuNavigationController: UINavigationController {
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// insert some code here but from MenuViewController class
}
override open func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// insert some code here but from MenuViewController class
}
}
I don't want to change UISideMenuNavigationController class because it is framework from pods.
I'm using framework side menu from https://github.com/jonkykong/SideMenu
I need to change button image whenever Side Menu Appear Or Disappear. I can't find the way from ReadMe Side Menu. That's why I think need to insert the code in ViewDidAppear and ViewDidDisappear Method from Side Menu Class but don't want to break the class.
You simply need to subclass UISideMenuNavigationController and override the viewDidAppear & viewDidDisappear methods to invoke a delegate.
protocol MyUISideMenuDelegate {
func menuDidAppear(_ menu:MyUISideMenuNavigationController) -> Void
func menuDidDisappear(_ menu:MyUISideMenuNavigationController) -> Void
}
open class MyUISideMenuNavigationController: UISideMenuNavigationController {
var menuDelegate: MyUISideMenuDelegate?
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.menuDelegate?.menuDidAppear(self)
}
override open func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.menuDelegate?.menuDidDisappear(self)
}
}
Then have you view controller with the button implement the protocol and set itself as the delegate.
You could also have your menu subclass send NSNotification and have any other objects that are interested subscribe to those. This way you completely decouple the menu and the other classes.
Your MenuViewController class could have a function that changes the image on the button. Eg, func changeButtonMenuImage().
Your 'UISideMenuNavigationController' (or a subclass of it) could have some sort of connection to the MenuViewController either as a property, instance variable or an IBOutlet. Eg, #IBOutlet var menuController: MenuViewController
Then your viewDidAppear and viewDidDisappear can call it's function. Eg, menuController.changeButtonMenuImage()
Related
I have a UINavigationControllerSubclass. When view controller is popped to some new view controller (by navigationController.popViewController, navigationController.popToRootViewController or even by manually sliding from left to right)
I need to call inside my navigation controller:
viewController.newTopViewController.updateBackButtonTitle()
What is the best approach to accomplish that?
One way of doing it would be the following:
class CustomNavigationController: UINavigationController {
override func popToRootViewController(animated: Bool) -> [UIViewController]? {
shouldUpdateBackButtonTitle()
return super.popToRootViewController(animated: animated)
}
override func popViewController(animated: Bool) -> UIViewController? {
shouldUpdateBackButtonTitle()
return super.popViewController(animated: animated)
}
private func shouldUpdateBackButtonTitle() {
viewController.newTopViewController.updateBackButtonTitle()
}
}
When you return to viewController call this viewWillAppear method. Inside that function you can check your rootviewController then you can call your
updateBackbuttonTitle()<
function.
You can use viewWillAppear method, and easily update UI Controls
super.viewWillAppear(animated)
What I'm trying to do
I'm trying to detect:
when a new UIViewController has entered the screen
when a UIViewController has left the screen
when a UIView has been added to the screen
when a UIView has left the screen
Key Point: I'm trying to detect all these changes from the outside.
Meaning: I don't want to have to respond to these changes from existing funcs inside the classes themselves, I want to be able to observe them from an outside class and react accordingly.
Bonus points: If we don't have to know ANY info about ANY of the UIViews or UIViewControllers beforehand, and we don't have to add ANY code to the views themselves, that would be amazing.
My initial thoughts is these involve KVO and listening to the UIViewController's view's window property and similar things.
You could make a BaseViewController class that inherits from UIViewController and overrides the viewDidAppear(_:) and viewDidDisappear(_:) methods. Within each of those you could post a notification.
Then make all UIViewControllers that you care about inherit from BaseViewController.
You still have to add code to all your ViewControllers, but it would just be an inheritance from a single class.
class BaseViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// viewDidAppear notification
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// viewDidDisappear notification
}
}
Same idea with a BaseView class.
class BaseView: UIView {
override func addSubview(_ view: UIView) {
super.addSubview(view)
// notification
}
override func willRemoveSubview(_ subview: UIView) {
super.willRemoveSubview(subview)
// notification
}
}
Say you have
var someVC: UIViewController
is it possible to essentially do the following, somehow?
get a notification when {
someVC has a viewWillAppear
self.#selector(wow)
}
#objc func wow() {
print("we spied on that view controller, and it just willAppeared"
}
Is that possible ?
(Or maybe on didLayoutSubviews ?)
(I realize, obviously, you can do this by adding a line of code to the UIViewController in question. That's obvious. I'm asking if we can "add on" to it from elsewhere.)
If I understand your question correctly, you want ViewController B to receive a notification when viewWillAppear is called in ViewController A? You could do this through the Notifications framework. Keep in mind that both VC's have to be loaded for one to receive a notification.
Alternatively, if the two VC's are on the screen at the same time, then I'd recommend a delegate pattern - have VC A tell an overarcing controller class that it's viewWillAppear has been called, and this overarcing controller will then inform ViewController B.
To do this using Notifications:
(This is from memory, so please excuse typos)
class TestClassA: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// To improve this code, you'd pull out the Notification name and perhaps put it into an extension, instead of hardcoding it here and elsewhere.
NotificationCenter.default.post(Notification.init(name: Notification.Name.init(rawValue: "viewControllerAppeared")))
}
}
class TestClassB: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(viewControllerAppeared(notification:)), name: Notification.Name.init(rawValue: "viewControllerAppeared"), object: nil)
}
#objc func viewControllerAppeared(notification: NSNotification) {
print("other viewcontroller appeared")
}
}
Documentation
class A : UIViewController {
var b = B()
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
print("inside view will appear")
print(b.transferText) // Here showing Goku only
}
}
And Second class
class B : UIViewController{
var transferText = "Goku"
...
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.transferText = "vegeta"
}
}
So I have to basically transfer the transferText from class B to A, when i click the UINavigationBar Back button. Any help would be appreciated .
Use static variable to do that
class B : UIViewController{
static var transferText = "Goku"
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
B.transferText = "vegeta"
}
}
in A class Use
print(B.transferText)
or for use after navigation back you can use completion handler , when navigation back just call that completion block with text.
Using delegation pattern would be a nice solution for passing data from child to parent (In your case - Class B to Class A) as you're saying Class A Has Class B instance and need to post data back to the parent when an event occurs.
1) Declare a protocol in Class B.
import UIKit
protocol ClassBDelegate
{
func didUpdate(strTransferText : String)
}
class B: UIViewController
{
// Should have valid connection from your Interface builder to this function.
#IBAction func navBarBtnUpdateClickked(_ sender: AnyObject)
{
// on Navigation bar click,Update transferText and fire the delegate again.
transferText = "Updated Goku by navigation bar button tap"
didTapNavigationbarButton();
}
var transferText = "Goku" // Default.
var delegate : ClassBDelegate? // Declare a public variable to store delegate.
override func viewDidLoad()
{
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
// Update transferText and fire delegate.
transferText = "Updated Goku by navigation bar button tap"
didTapNavigationbarButton(); // fire delegate from ViewWill appear.
}
private func didTapNavigationbarButton() -> Void
{
// Need to notify Instance of A class from here.
// Using Delegation pattern!
if(delegate != nil)
{
delegate!.didUpdate(strTransferText: transferText);
}
}
}
2) Confirm to ClassBDelegate in Class A and implement the delegate.
// Confirming to ClassB Delegate
class A: UIViewController,ClassBDelegate
{
var b : B?
override func viewDidLoad()
{
super.viewDidLoad()
// Setup a segue in storyboard to move from class A to Class B to get class B Instance.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
b = segue.destination as? B
b?.delegate = self
print("inside Prepare for segue function of Class A")
print(b?.transferText) // Here showing Goku only(Default value).
}
internal func didUpdate(strTransferText: String)
{
print("Updated transferText in Class B " + strTransferText);
}
}
3) There you go,You'll get call backs for Class A instance whenever button pressed in Class B.
you can achieve it by other ways like Observer pattern(NSNotificationCenter) or KVO pattern and even with Singleton pattern(Not applicable always).
Hope this helps to explore the best fit for your requirement.
Happy to help :)
In class B, you declare property for A class
var a = A()
And in the B's viewWillDisappear, put
a.transferText = "vegeta"
(you declared property transferText in A class).
I have UIViewController and I created my custom class for its view. By the way, I redefined method "canBecomeFirstResponder" in that class so it always returns true. I want my view to become first responder when the viewDidAppear method is called. But after I go to another controller and come back my program crashes. Can not understand why does this happens. Here is some code:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.firstResponder = self.view.isFirstResponder()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if(self.firstResponder){
self.view!.becomeFirstResponder()
}
}
This link helped me a lot. https://stackoverflow.com/a/21906038/4092466 I should not have named the property "firstResponder". When I renamed it everything worked fine.