Having trouble avoiding subclassing - ios

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
}
}

Related

Parent VC delegate methods not called when embedded containerVC methods are called

I have a Parent VC with a Child VC embedded in a container. Both VCs conform to delegate, but only the child delegate methods are called. How can I get both VC's delegate methods to respond? Am I missing something with delegate pattern for container views? Thanks in advance for any help.
Central class:
public protocol BLEManagerDelegate: class {
func bLEManagerShowAlert(message: String)
}
public class BLEManager: NSObject {
static let sharedInstance = BLEManager()
weak var delegate: BLEManagerDelegate?
public func postMessage() {
delegate?.bLEManagerShowAlert(message: message)
}
}
ParentVC
class HomeVC: ContentViewController, BLEManagerDelegate {
var bLEManager = BLEManager.sharedInstance
override func viewWillAppear(_ animated: Bool) {
bLEManager.delegate = self
}
// delegate methods
func bLEManagerShowAlert(message: String) {
// THIS METHOD IS NOT GETTING CALLED
}
}
Container view embedded into ParentVC
class ChildVC: UITableViewController, BLEManagerDelegate {
var bLEManager = BLEManager.sharedInstance
override func viewWillAppear(_ animated: Bool) {
bLEManager.delegate = self
// delegate methods
func bLEManagerShowAlert(message: String) {
// This method IS getting called
}
}
Your delegate property can only hold a reference to one object at a time. As soon as your ChildVC sets itself as the delegate, the parentVC is no longer the delegate.
If you want to notify multiple objects you could look at using NotificationCenter
Why do you need Singleton BLEManager? Where do you call postMessage()? If alerts are displayed in their own view controllers just write default implementation for a default alert message via protocol extension. Then just implement the methods in VCs for custom messages. If you want multiple delegates you should try this: http://www.gregread.com/2016/02/23/multicast-delegates-in-swift/

Use extension for my certain View Controller

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 {
}

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.

'UITabBarDelegate' cannot be constructed because it has no accessible initializers

I came from a background in Android development to the iOS world.
I'm trying to avoid those odd patterns (at least for a Android dev) that connects storyboard items directly to my controller (like #IBOutlet).
I want to know if it's possible to create an anonymous function to delegate some events of a UITabBar:
let tabBarDelegate = UITabBarDelegate {
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem!) {
label.text = item.title
}
}
tabBar.delegate = tabBarDelegate
The error I'm facing is this one: 'UITabBarDelegate' cannot be constructed because it has no accessible initializers.
I'm really new to this world. How can i accomplish that?
You can't instantiate a UITabBarDelegate because it is a protocol (similar to an interface in Java), not a class.
You have to have one of your classes declare that it implements UITabBarDelegate, then set an instance of that class as the tab bar's delegate.
Here's a short example:
class MyViewController: UIViewController, UITabBarDelegate {
//all of UITabBarDelegate's methods are optional, so you don't have to implement them
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem!) {
}
}
var viewController = MyViewController()
var tabBar = UITabBar()
tabBar.delegate = viewController
Also, I don't believe you can create anonymous classes in Swift.

How to pass data between two ViewControllers with TabBarController in Swift

I have a tab bar class (that is attached to my tab bar controller), Like so:
class CaptionTabBarController: UITabBarController, UITabBarControllerDelegate {
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
var logView = self.viewControllers![2] as CaptionsController
logView.log.append("test working!")
}
override func awakeFromNib() {
self.delegate = self;
}
}
And my receiving viewcontroller is like this:
class CaptionsController: UIViewController {
#IBOutlet weak var captionSearchBar: UISearchBar!
#IBOutlet weak var captionsTitle: UILabel!
var receiveImage:UIImage!
var receiveCategoryText:String!
var log = [String]()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
println(log)
}
}
This works when I'm explicitly setting logView.log in CaptionTabBarController.
The result I get in my output windows is as expected. Each tabbar item I click adds "test working!" to the array.
My question is:
How would I be able to get a value from another viewcontroller class to CaptionsController using the tabBarController method I am employing?
This view is a part of a "child" of the tabbar itself, so I'm assuming it already has an instance. All examples I've found just show this, but not how to get data from another class.
The UIViewController that wants to pass the data can store it on your AppDelegate class. Then the UITabBarController delegate method can pull it off and set properties on the receiving UIViewController.
Also, assuming your app is based on the Tab Controller, your AppDelegate can find it with window?.rootViewController as UITabBarController.

Resources