iOS Swift Xcode 6 separation of concerns for view controller delegates in storyboards - ios

I'm having a lot of fun learning iOS dev and Swift, but having a spot of bother with this.
I'm writing a view with a bunch of controls in it, and my UIViewController is getting a bit bloated with all the delegate protocols it has to support. So I want to break out some of the delegation to separate classes. But there doesn't seem to be any way of connecting the view controls to the separated classes in the storyboard?
Example: I did have:
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{
...code...
}
and it was easy to connect the picker view to this by dragging the connectors to the ViewController thingy in the top margin of the view in the storyboard, like so:
now I have:
class ViewController: UIViewController{
...code...
}
and in a separate file:
class PickerHandler: UIPickerViewDelegate, UIPickerViewDataSource{
...code...
}
However, there's no way I can drag the delegate connectors for the picker view to this class, so how do I wire up the delegate?

Quick answers: Like snowman4415.
Swift recommended way:
class ViewController: UIViewController {
...code...
}
extension ViewController: UIPickerViewDelegate {
}
extension ViewController: UIPickerViewDataSource {
}
You can put these extensions anywhere you want. There is no need to make another class for this, assuming you just don't want your class becomes too big.
Also, there are other cases that require making another class, like you did, but it requires more works, like when you have to write extra protocol in order to delegate from PickerHandler back to ViewController (because you can't call ViewController's instance inside PickerHandler class directly)

Related

One storyboard for few view controllers

I have a storyboard, and it needs to be used in few places. Is there any other option than just duplicate storyboard and assign it to new view controller? I want to avoid boilerplate code and writing 3x same outlets and others. I need exactly same layout and almost all functions in all three controllers, but I want to only change model for all of them.
I have tried to do it like this:
class FirstViewController: UIViewController {
//outlets
// code
// tableView configuration
}
class SecondViewController: FirstViewController {
// and here I have just overrided some function to modify them
}

How can I create an instance of a custom model with multiple viewcontrollers in Xcode with Swift 4?

I'm new to Swift and I'm sure this question is pretty basic and has been asked and answered before.
I am not using storyBoard. My main viewController is created from AppDelegate via code.
I have:
a custom class defined in a model.swift file
a main viewController (from AppDelegate) that I am using as a container
3 additional viewcontrollers as subviews of the main (not each other)
all 3 subviews are displayed simultaneously each covering 1/3 of the screen (no segues)
each viewcontroller is in a separate .swift file
I want to create an instance of my custom class in the main viewController and have all 3 of the subviews be able to reference that instance.
Each of the subview view controllers need to be able to get/set instance variables and the other subviews need to be made aware of those changes.
I think I will need to use notifications to communicate the changes to the multiple subviews - but I haven't even begun to try and figure that out yet.
If this has been asked and answered before - could someone please either provide a link - or provide me with the right search terms so that I'm able to find the answer? The only found answers I've found that come close are to use segues to pass the data back and forth.
You can use delegate pattern. Below code is assuming that you are using MVVM pattern. (It is very similar for VIPER/ReSwift patterns also)
protocol DataChangedDelegate {
func refreshData()
}
// ViewModel for FirstViewController
class FirstViewModel {
var delegate: DataChangedDelegate?
var data: Any {
didSet {
delegate?.refreshData()
}
}
//rest of the things
}
//similarly other two view models will have a delegate and on data change will call the refresh method
And your view controllers should adopt this protocol
class FirstViewController: UIViewController, DataChangedDelegate {
//view controller code
//delegate code
func refreshDate() {
//tableView.reloadDate()
//collectionView.reloadDate()
//lableView.text = viewModel.data()
}
}
And where ever you create a viewControllers and add as subView, you have to set the delegate of viewModel.
let firstViewController: FirstViewController = createFirstViewController()
let firstViewModel = FirstViewModel()
firstViewModel.delegate = firstViewController
firstViewController.viewModel = firstViewModel
mainViewController.addSubView(firstViewController.view)
Similarly for all other view controllers.
Here's how I would do it:
Create a singleton class.
Configure the singleton's properties in the the main ViewController.
Use didSet to post a Notification.
Add a listener for that Notification in your additional ViewControllers.

Swift: hook into lifecycle functions of UIViewController via protocol with default implementation

Is it somehow possible to have a protocol with a default implementation via protocol extension to get some code automatically executed by just implementing the protocol in various places?
Heres an example of what I mean:
protocol Greetable {
func greet()
}
extension Greetable where Self: UIViewController {
func greet() {
print("Hello ViewController")
}
func viewDidLoadMagicFunctionHookIdontKnowIfItIsPossible() {
greet()
}
}
class MyViewController: UIViewController, Greetable {}
So I want to define a protocol, give it default implementations and then hook the extension to let's say viewDidLoad() or other functions without explicitly calling the greet function by myself in every instance.
Subclassing is not possible since I want to use UITableViewController, UIViewController, UINavigationController, UISplitViewController, etc. and I cannot create one generic baseclass for all of them at once.
I also don't want to go through all of my view controllers and call greet() in viewDidLoad() because what if I decide to move the greet() to viewDidAppear? I had to go through all the controllers and move the code.
By the way, the order of when this magic function would be called would not matter at all for me, in case I have two protocols with this magic.
I'm surely not the only one who thought about something like this and there are programmers a thousand times smarter than me who surely can exactly explain why and how or why it is not possible in Swift to "hook" yourself into a certain function call.

Using the same property name/reference for Outlets in a base class and potential bugs

So I have a base class UIViewController called UITabBarTopLevelViewController:
class UITabBarTopLevelViewController: UIViewController {
#IBOutlet weak var uiNavItemTitle: UINavigationItem!
override func viewDidLoad() {
super.viewDidLoad()
uiNavItemTitle.titleView = CoreUtility.LogoForUINavBarGet()
// Do any additional setup after loading the view.
}
I then have two UIViewControllers that inherit from my base class and both look like this, except the second is called MyViewController2:
class MyViewController1: UITabBarTopLevelViewController {
//#IBOutlet weak var uiNavItemTitle: UINavigationItem!
override func viewDidLoad() {
super.viewDidLoad()
//uiNavItemTitle.titleView = CoreUtility.LogoForUINavBarGet()
}
I add a Navigation Bar object to each child UIViewController super view and then I CTRL drag and add a new outlet to each UIViewController child class:
And here is the second CTRL drag outlet:
These are different references, but I can comment out the #IBOutlet weak var uiNavItemTitle: UINavigationItem! in my child classes and reference one time only in the base class UITabBarTopLevelViewController, for both MyViewController1 and MyViewController2.
You can see, when I hover over the outlet circle, in my base class, it highlights both UINavigationItem in the Story Board:
I am surprised this works, its nice for me that it works because I only need to set the uiNavItemTitle.titleView for my logo one time for both views. This is what I want but there seems to be a bit of magic that I can reference multiple outlet references one time in my base class and there is no bugs or crashes.
I currently have no memory leaks or crashes and my app is working just fine and doing exactly as I desire.
Will there be any bugs with this?
Could someone explain how this is working?
Is the fact that this works, not surprising to experienced Swift developers?
That's how subclass exactly works.
You placed a UINavigationItem in the base class UITabBarTopLevelViewController through
uiNavItemTitle.titleView = CoreUtility.LogoForUINavBarGet()
Also, MyViewController1 and MyViewController2 inherit from the base class UITabBarTopLevelViewController. That's say these child viewControllers both have a UINavigationItem which inherit from their UITabBarTopLevelViewController.
This is not a bug, on the other hand, more like a topic about design pattern though. You could place all the base stuff into a base class, inherit from those classes and implement the specific detail within the child class.
HTH.

Swift: Change Type of View Controller to Something Other than UIViewController

I'm, relatively, a beginner, so this may be an entirely common practice—or an entirely impossible one—but I've been wondering if it's possible to modify a view controller added in a storyboard so that instead of (or in addition to?) being an instance of UIViewConroller, it's also an instance of (blahblah)ViewController, e.g. ABUnknownPersonViewController.
That way, instead of doing something like this:
class ViewController : UIViewController {
override func viewDidLoad() {
let test = ABUnknownPersonViewController()
...
self.presentViewController(test, animated: false, completion: nil)
}
}
This could be done:
class ViewController : ABUnknownPersonViewController {
override func viewDidLoad() {
//ViewController already is an ABUnknownPersonViewController, so you can treat it as one
//example below (displayedPerson is a property of ABUnkownPersonViewControllers)
self.displayedPerson...
}
}
EDIT: ABUnknownPersonViewController is a class supplied by Apple, which does not support subclassing (here). With that said, and the understanding that I would obviously like as simple a solution as possible (avoidance of protocols and whatnot), what are my options?
I tried class FourthViewController: UIViewController, ABUnknownPersonViewController, ABUnknownPersonViewController, ABUnknownPersonViewControllerDelegate only to get an error about multiple inheritance.
It sounds like what you actually want to do is to subclass UIViewController, and in your storyboard, set the custom class to your subclass. When the view controller is loaded from the storyboard, it will be an instance of your subclass.
So your subclass would look like this:
class ABUnknownPersonViewController : UIViewController {
override func viewDidLoad() {
self.displayedPerson...
}
}
In the storyboard, highlight the view controller you want to use a custom class for, and in the Custom Class field, type the name of your subclass. If you've done it correctly, it should autocomplete for you.

Resources