Use extension for my certain View Controller - ios

I have created a class used for an extension at my certain view controllers,
class STPopupTransitionAnimator: NSObject, STPopupControllerTransitioning {
....
}
and now I would like to set it to my View Controller, so I must not use the same code in my Home, profile, browse, search, etc like this:
extension HomeViewController: STPopupControllerTransitioning {
func popupControllerTransitionDuration(_ context: STPopupControllerTransitioningContext) -> TimeInterval {
return context.action == .present ? 0.9 : 0.4
}
}
The question is, how could I use my STPopupTransitionAnimator for my home, profile, and search view controller ?
What I don't want to do is to extend my UIViewController like this:
extension UIViewController: STPopupTransitionAnimator {}
because I only used that protocol only at certain view controllers and I used it to check it for something.

create a subclass of UIViewController like
class STPopupViewController: UIViewController, STPopupControllerTransitioning {
//all functions implementations
}
and subclass your UIViewControllers from it
class HomeViewController: STPopupViewController {
}

Related

Having trouble avoiding subclassing

I have an app with multiple view controllers. I am implementing a search bar to navigate a table view that is in each of these view controllers.
I have chosen to implement the search controller using a custom class, where I handle all the search logic. In order to make this possible, I am currently using a superclass from which each view controller inherits. I would like to know if there is a way for me to make this work without subclassing.
Here is the current implementation of my SearchController class:
class SearchController: NSObject, UISearchBarDelegate {
/* This is the trouble spot. If I change this to UIViewController?,
I get the compiler error "value of type UIViewController has no member tableView" */
weak var viewController: BaseViewController?
/*
... rest of SearchController implementation
includes methods that interact with view controller table views
*/
}
this is the BaseViewController class:
class BaseViewController: UIViewController {
let searchController = SearchController()
let tableView = UITableView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
/*
... rest of BaseViewController implementation
*/
}
To summarize, the issue I am having is that I have several view controllers with tableviews and I can't seem to make this work without creating a new base class that they can inherit from. Using UIViewController simply won't work because the UIViewController class does not have a tableView property built into it.
Any ideas?
You do not need to force all your viewControllers to subclasses BaseViewController. If the only requirement is for the viewController to have a tableView property then define a protocol with that requirement and make the relevant viewControllers implement that protocol.
Rewriting your example:
protocol BaseControllerProtocol: class {
var tableview: UITableView { get }
}
class SearchController: NSObject, UISearchBarDelegate {
//We store any class that implements the BaseControllerProtocol protocol
//Now you can use viewController.tableview
weak var viewController: BaseControllerProtocol?
//If you what to have UIViewcontrollers instances only use:
//weak var viewController: (UIViewController & BaseControllerProtocol)?
}
//An example of a viewcontroller that implements the BaseControllerProtocol
class ARandomViewController : UIViewController, BaseControllerProtocol {
var tableview: UITableView = UITableView()
}
I think you could still use UIViewController? if you assign its UITableView to your SearchViewController's private tableView calculated instance variable like so:
class SearchController: NSObject, UISearchBarDelegate {
weak var viewController: UIViewController?
fileprivate var _tableView: UITableView? {
if let vc = self.viewController {
for subview in vc.view.subviews {
if let tableView = subview as? UITableView {
return tableView
}
}
}
return nil
}
// Whatever methods interact with table view should now use self._tableView.
func doSomething() {
guard let tableView = self._tableView else { return }
// Do something with the tableView
}
}

UIViewController base class - is it good or not

I have to apply single logic for gesture recongnizer to all UIViewController elements in my app.
I thought of creating a base class. However, as far as I know we should add extra layer to inheritance chain only in purpose of strong necessity.
What is the most efficient way to add functionality I mentioned above to my project, without copy-pasting code to each of mine controllers?
You can use extension of UIViewController
extension UIViewController
{
// write code that has to be used in all view controllers
func applyTheRequiredPropertiesToGestureRecognizer(gr:UIGestureRecognizer)
{
// do whatever you want to do
}
}
Later while using
class SomeViewController:UIViewController{
let gestureRecog = UIGestureRecognizer()
//call the method
func someMethod()
{
self.applyTheRequiredPropertiesToGestureRecognizer(gestureRecog)
}
}
Make a baseviewController and do all the functions which are generic or used at most of the view controllers like an alert.
class BaseViewController: UIViewController {
func yourGestureRecogniser() {
// do the process
}
}
Then inherit the base view to other view controllers like this,
class ViewController1: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
yourGestureRecogniser()
}
}
class ViewController2: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
yourGestureRecogniser()
}
}
customize the gesture function as per your needs.
Using base controller is certainly a common approach.
A common base controller often becomes a dumping ground for functionality that is used in multiple-but-not-all controllers, so it become overkill.
I also followed this pattern in my project but later it becomes complicated for me when i start using other controllers like UITableViewController, UICollectionViewController etc.
Agreed with Sulthan's comment: using a category and turn it on explicitly on every controller would definitely be a better option.

Can extension replace delegation in swift?

I am learning delegation in swift. This paragraph uses example to explain how delegation works.
In this simple example, I want to ensure that my app’s root view
controller, a UINavigationController, doesn’t permit the app to rotate
— the app should appear only in portrait orientation when this view
controller is in charge. But UINavigation‐ Controller isn’t my class;
it belongs to Cocoa. My own class is a different view controller, a
UIViewController subclass, which acts as the UINavigationController’s
child. How can the child tell the parent how to rotate? Well,
UINavigationController has a delegate property, typed as
UINavigationControllerDelegate (a protocol). It promises to send this
delegate the navigationControllerSupportedInterfaceOrientations
message when it needs to know how to rotate.
My question: Can we extend myOwnViewController to have method "navigationControllerSupportedInterfaceOrientations" to replace the delegation pattern to achieve the same goal?
You can extend myOwnViewController, tell extension to conform to UINavigationControllerDelegate protocol and implement method that you want, however this does not replace the delegation pattern. If your extension does not conform to this delegate's protocol, you won't be able to attach myOwnViewController as UINavigationController's delegate.
class MyController: UIViewController {
}
extension MyController: UINavigationControllerDelegate {
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
}
let navigationController = UINavigationController()
let controller = MyController()
navigationController.delegate = controller
Same result can be achieved by telling MyController class to conform to UINavigationControllerDelegateprotocol.
class MyController: UIViewController, UINavigationControllerDelegate {
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
}
let navigationController = UINavigationController()
let controller = MyController()
navigationController.delegate = controller
Those two examples result in MyController being the delegate for your UINavigationController. This can lead to having many responsibilities in one place. In order to split this responsibility, you can create another class that will be the delegate for your navigation controller.
class MyController: UIViewController {}
class NavigationDelegate: NSObject, UINavigationControllerDelegate {
func navigationControllerSupportedInterfaceOrientations(navigationController: UINavigationController) -> UIInterfaceOrientationMask {
return .Portrait
}
}
let navigationController = UINavigationController()
let controller = MyController()
let navigationDelegate = NavigationDelegate()
navigationController.delegate = navigationDelegate
What is the difference? Imagine that you have implemented a complex logic for your supported orientation method and you find out that you don't use MyController any more, instead, you will use DifferentController. Since your orientation logic is in MyController, you would need to create it anyway. However, if your delegate logic is separated in NavigationDelegate class, you can use DifferentController without need of creating MyController class just for these delegate methods.

Access tab controller views?

I started an app with a single view controller. I've since added a tab controller view and created 4 views that are setup as tabs, and that all seems ok.
The only viewcontroller.swift file I have is the original one. I'm not sure how to access the view controllers for each of the individual tabs.
Should I just use the one viewcontroller.swift for all my code and link the controls in each tab back to it?
To access the other view controllers, create classes of your UIViewController, like so. I'd recommend you put each class in a separate Swift file in your project, but it's not necessary.
class SecondViewController: UIViewController {
...
}
class ThirdViewController: UIViewController {
...
}
class FourthViewController: UIViewController {
...
}
Then assign them in the Identity Inspector by clicking each Storyboard UIViewController:
I make tabViewController just like this. Make my own viewcontrollers. First, init my own viewcontrollers. Then, push them into tabViewController.viewControllers
class TabbarController: UITabBarController {
override func viewDidLoad(){
super.viewDidLoad();
let first = YourFirstViewController();
let second = YourSecondViewController();
let thrid = YourThirdViewController();
self.viewControllers = [first, second, third];
}
}

Swift - IBAction declared in super class is not reflected in subclass in storyboard

I have implemented a subclass of UIViewController like this:
import UIKit
class LoadImageViewController: UIViewController {
#IBAction func loadImageAction(sender : AnyObject) {
}
}
Then I implemented subclass of LoadImageViewController like this:
class ProfileSnapViewController: LoadImageViewController {
// an empty implementation
}
In storyboard, I assigned class of a UIViewController as ProfileSnapViewController, and then I tried to bind IBAction declared in its superclass to a button, but to my surprise it is not showing the action when I right clicked on the file owner.
Any ideas?

Resources