Adding An UITableViewController on to a SubView - ios

An UITableViewController pretty much takes up the entire view. I need a way to limit its height, width and add some shadows etc. For a clear explanation, I won't show the UITableViewController's contents.
Without the use of a storyboard, I subviewed the UITableViewController:
// In another UIViewController
let otherController = OtherController() // A subclass of UITableViewController
let otherControllerView = otherController.view
someView.addSubView(otherControllerView)
[...] // bunch of constraints
Notes:
In AppDelegate, if I set the rootController as OtherController(), everything works as it should. If I change it back to SomeView(), I see my modified tableView. If I should click it, it disappears.
This was the only thing that came close to my issue but sadly, I could not understand the answers provided as nothing made any sense to me.
I need to understand, why it disappears when touched etc.
view.bringSubviewToFront(...) proved futile. I'm gessing that a tableView should be rendered in its own controller and not in another view?

So just to answer this question, indeed you got two options. One is the best way, as suggested by Rakesha. Just use UITableView. Add it as a subview. Done.
And in the future, if you really want any controller to be added onto any UIView, remember you need to add that controller as a child. For example, in your case:
The controller of the view that will hold your UITableViewController will add such UITableViewController as a child.
self.addChild(yourUITableViewController)
self.whatEverViewContainer.addSubview(yourUITableViewController.view)
// Take note of the view of your tableViewController above^.
// Then setup the constraints of your yourUITableViewController.view below.
I hope this helps!

You must add the instance of UITableViewController's subclass as child view controller of the other view controller. You need to ensure few points in order to make it work. The points are as listed below:
Create the instance of your TableViewController
Add it as a child view controller of the other view controller
Add its view as a subview of the desired view (you may do these steps in viewDidLaod since they need to be done only once)
Keeping in mind the view cycle of a view controller. You must keep a weak reference of the child view controller aka TableViewController to adjust its view frame after the parent view controller has laid its subviews.
Code here:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let vc = TableViewController()
addChildViewController(vc)
view.addSubview(vc.view)
vc.didMove(toParentViewController: self)
childVC = vc
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
childVC?.view.frame = view.frame
}

Related

Multiple ViewControllers contained in a UIStackView

I have a UIStackView and I am dynamically adding UIViewControllers contained, here is my code;
[self addChildViewController:driverForm];
[self addChildViewController:marketingView];
[self.stackView insertArrangedSubview:driverForm.view atIndex:0];
[self.stackView insertArrangedSubview:marketingView.view atIndex:1];
[driverForm didMoveToParentViewController:self];
[marketingView didMoveToParentViewController:self];
After reading the documents it states I must call didMoveToParentViewController.
The problem I am facing is, the actions on the final UIViewController are not being called, but the first ViewController does. If I swap these round the first one works and the last one does not.
Simply add the view of your ViewController to your UIStackView like this:
yourStackView.addArrangedSubview(yourViewController.view)
Also, you don't need to be worried about the view being nil as it always returns UIView!
Note that the order is reversed, so the last appears first. To address this, assuming you have an array of view controllers, you can use stride to traverse your array inversely and add view controllers to your stack.
Here is a quick copy/pasta version for Swift 5:
private var childViewController: UIViewController
private var stackView: UIStackView?
// MARK: - UIViewController
override func loadView() {
super.loadView()
// 1. Add the child view controller to the parent.
addChild(childViewController)
// 2 Create and add the stack view containing child view controller's view.
stackView = UIStackView(arrangedSubviews: [childViewController.view])
stackView!.axis = .vertical
self.view.addSubview(stackView!)
// 3. Let the child know that it's been added!
childViewController.didMove(toParent: self)
}
UIStackView is for arranging multiple subviews in the same UIViewController class. how can you use it for different UIViewControllers?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/#//apple_ref/doc/uid/TP40015256-CH1-SW31

adding a uipageviewcontroller to a uistackview

I'm trying to add a sliding photo gallery functionality to the top portion of a view.
To give context, a user taps on a button or row or something. Then i load a scrollview with a uistackview in it. organized vertically, i had an image, and then another stack view with some text in it. Now, i want that image to become part of a larger "gallery". My research told me to implement UIPageviewcontroller and add the other images to a childVC.
i used this as a tutorial (the first example): http://www.raywenderlich.com/76436/use-uiscrollview-scroll-zoom-content-swift
the only relevant deviation from the tutorial my app has is that it creates things programmatically.
With my proof of concept for the gallery functionality, i wanted to integrate it with the previously mentioned stack view. my plan was to first add the pageviewcontroller stuff into the overall stack view with the original image view right below it and then simply remove the original image view to leave me the final product.
i was able to add the pageviewcontroller.view to the stackview, but the gallery doesn't show. taking a look at the UI Inspector, i can see that the gallery is kinda loaded, but it's messed up.
it's as if the uiview has a frame of 0 height and so the other stack view items don't respect the images that the pageviewcontroller is trying to show.
I think it could be that stack views can only handle specific views, not stuff as complicated as pageviewcontrollers.
also note: my implementation is all programmatic, no storyboards, and so for no xibs. so maybe i missed something here.
here is some code, if it helps:
note the constrain functions you see are from the "cartography" pod
this adds the "gallery" to the stack view, it's a delegate function from my view
func addZoomStuff(sender: UIStackView) {
let zoomer = PageBaseViewController()
addChildViewController(zoomer)
zoomer.view.translatesAutoresizingMaskIntoConstraints = false
zoomer.view.tag = 5
sender.addArrangedSubview(zoomer.view)
zoomer.didMoveToParentViewController(self)
}
this is what creates the scrollview, image view, etc for the gallery items:
override func viewDidLoad() {
super.viewDidLoad()
//MARK: - Zoom View Elements
// prep
scrollView = UIScrollView(frame: self.view.frame)
scrollView.delegate = self
self.view.addSubview(scrollView)
constrain(scrollView, view) { view, view2 in
view.edges == view2.edges
}
self.view.setNeedsUpdateConstraints()
// 1
let image = UIImage(named: imageName)!
imageView = UIImageView(image: image)
// 2
scrollView.addSubview(imageView)
constrain(imageView){ view in
view.edges == view.superview!.edges
}
scrollView.contentSize = image.size
i tried adding the constraints like this but there was no effect
func addZoomStuff(sender: UIStackView) {
let zoomer = PageBaseViewController()
addChildViewController(zoomer)
view.addSubview(zoomer.view)
constrain(zoomer.view, view) { view, view2 in
view.width == view2.width
view.height == view2.height * 2 / 3
view.leading == view2.leading
view.top == view2.top
}
zoomer.view.setNeedsUpdateConstraints()
zoomer.view.removeFromSuperview()
zoomer.view.translatesAutoresizingMaskIntoConstraints = false
zoomer.view.tag = 5
print("sender.subviews: \(sender.subviews)")
sender.addArrangedSubview(zoomer.view)
zoomer.didMoveToParentViewController(self)
print("sender.subviews: \(sender.subviews)")
}
if this method isn't going to work, can i do a nested horizontal stack view instead of the pageviewcontroller and somehow get that same scrolling/snap effect to see on image view at a time?
TLDR;
Create a subclass of UIPageViewController, make it it's own delegate.
Initialize the subclass with a plain UIViewController, only set a backgroundcolor.
In the pageviewcontroller subclass, implement the two delegate callbacks for a next and previous viewcontroller: create a plain viewcontrolller, with some random backgroundcolor.
If this works, replace the plain viewcontroller by your actual contentviewcontroller.
Long version:
Have you seen this: Maybe this link will help: https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/PageViewControllers.html
It might help, as it explains the details of UIPageViewController. Basically, you need to create a viewController (not a view!), that shows one page of the gallery. So this VC has a stackview, and manages the content of it. The pageviewcontroller is initialized with your first contentviewController. If you create a subclass of the uipageviewcontroller, you can set self of that subclass as the delegate of it. Implement the delegate callbacks that return the next or previous viewController and thats it. For this last part, it is convenient to have a property on the contentviewcontroller from which the subclasses of the pageviewcontroller can figure out what data to set on the next or previousviewcontroller.
Your title seeks to hint at some confusion: its not possible to add a viewcontroller to a view. You can only add other views to a (stack)view. A viewcontroller owns and manages a viewhierarchy. A pageviewcontroller has no content, but manages the insertion and removal of viewcontrollers. as the pageviewcontroller is a containerviewcontroller, it will als take the contentViewcontrollers' views and place them in the viewhierarchy. But this is not something your code has to do when you subclass UIPageViewControlller and implement it's delegates on itself (and don't forget to assign self to be the delegate).

Insert subview behind TabBar from child view of TabBarController

I have TabBar with 2 tabs. At some point, from either of the 2 tabs, I want to add a view that is visible on both tab views but behind the TabBar.
So I thought, insert a subview into the TabBarController but below the TabBar.
This works fine in principle and I have the view behind the TabBar but now covering my 2 tabs as I wanted. However, it doesn't actually load. Just its background loads and only viewDidLoad() is called, not viewWillAppear() or any others.
I have also tried calling addChildViewController(myVC) on the TabBarController which has no effect, and also manually calling viewWillAppear() on the view controller I add which also has no effect (and I'm also dubious about whether manually calling viewWillAppear() is permitted or not?).
Is what I'm trying to do possible? What am I missing? Or should I be attempting this some other way?
For some reason, when inserting a subview into a UITabBarController behind it's UITabBar, although the view is visible to the user, the system itself seems to think it is not and so although viewDidLoad() is called, viewDidAppear() and subsequent methods are not.
However, adding a subview above the UITabBar seems to work fine. So I solved this by adding my own new UITabBar as a subview to the UITabBarController (set up basically exactly as the default one would be) and then removing the UITabBarController's default UITabBar.
Then when later inserting my view into the UITabBarController, I insert it as I was doing originally but instead below/behind my custom UITabBar and it seems to load fine.
There is no need to remove and recreate the tabBar. What you need to do is after you insert your custom view, you can then bring the tabBar to the front again.
//bring the tabBar to the front after inserting new view
self.view.bringSubview(toFront: self.tabBar)
This would be a good way:
Add the function below and call it in viewDidLoad of your initial VC. It unwraps your tab bar controller instance (which is optional), and then inserts the view you always want visible just below the tab bar.
private func setupAlwaysVisibleView() {
guard let tabBarController = self.tabBarController else { return }
tabBarController.view.insertSubview(alwaysVisibleView, belowSubview: tabBarController.tabBar)
}
Avoid using optionals for tabBarController or removing current tabBar. Simple add your view below tabBar view. Swift 5, XCode 11.
class TabBarController: UITabBarController {
#IBOutlet var instructionsView: UIView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.view.insertSubview(instructionsView, belowSubview: self.tabBar)
}
}
you can also do this inside the init() method for your UITabViewController:
view.insertSubview(alwaysVisibleView, belowSubview: self.tabBar)
no need to dispatch to another method if you are using a subclass of UITabViewController.

iOS - is it possible to inherit view controller with xib file? and how?

I have a few view controllers which would have same background image and one or two buttons. Rest of the content would be different for each controller. I would like to create BaseViewController which would have .xib file and in which I would set background image, buttons and other stuff with constraints. Then I would like create subcontrollers (HomeViewController, GameViewController and so) which just inherit from BaseViewController and have all stuff set in Interface Builder. Is it possible? And subcontrollers would have set own stuff in Storyboard? Background image set in .xib for superclass and tableView in Storyboard for subclass. I know it would be possible when I would set all stuff in code but is it possible with .xib and IB?
I was thinking about usage of Container view but it's possible that I would like to change BaseViewController and maybe create more supercontrollers so I think if it is possible it would be easier with inheritance.
Edit:
Possible way suggested by iphonic. It's based on two controllers that together enabled other controllers to be subclass. BaseGameDesignViewController has .xib file and no more code in controller other than default. Code below is from BaseGameViewController from which inherits other controllers. This has problem with unwind segue which when rolling down has white screen.
override func viewDidLoad() {
super.viewDidLoad()
var viewController = BaseGameDesignViewController(nibName: "BaseGameDesignViewController", bundle: nil) as BaseGameDesignViewController
contentView = viewController.view
viewController.homeButton.addTarget(self, action: "homeButtonTapped:", forControlEvents: .TouchUpInside)
self.view.insertSubview(contentView, atIndex: 0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentView.frame = self.view.frame
}
func homeButtonTapped(sender: AnyObject) {
self.performSegueWithIdentifier("backToMainSegue", sender: self)
}
This is impossible.
The best solution you can achieve is either based on:
Container views
having a dedicated view XIB which is then included into every controller
Outlets defined in superclass but copy-pasting everything in Interface Builder.
you can avoid code duplication but you won't avoid duplication in IB.
No, it is not possible to inherit view controllers with .xib files.
However, you can use Container View for reusing subviews for your so called child views. You have to create common subviews used in different view controllers in a container view.Click here

tableView initwithframe and storyboard

I'm trying to add a tableview as subview to my tableViewController, but I want to setup the cells in storyboard. It will be a static tableview.
This is the code for calling the tableview on button click.
- (IBAction)botaoAdicionar:(id)sender {
AtividadesPraticadasTableView *tableview = [[AtividadesPraticadasTableView alloc] initWithFrame:CGRectMake(0, 170, 320, 320) style:UITableViewStylePlain];
[self.view addSubview:tableview];
}
In the tableview class I have this:
#implementation AtividadesPraticadasTableView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
Now, I have a viewcontrollerin storyboard with a tableview, which the class of the tableviewI changed to this file AtividadesPraticadasTableView. It has three static custom cells in storyboard, therefore it opens a blank default tableview.
What am I missing?
Static table views are entirely contained within the storyboard, and require information from the storyboard to display their content.
You've defined a static table view controller in the storyboard, populated it and set the tableView's custom class to your custom class, but when you want to add the table view you are just instantiating a table view of that class. That isn't going to get any of the information you've added to the storyboard.
In addition, the static cells information is read and understood by the UITableViewController, not the UITableView, so you are at the wrong level there too.
You need to do the following:
Get a reference to the storyboard, either from your original view controller's storyboard property (if it is on the same storyboard as your static table) or using storyboardWithName:bundle:.
instantiate the table view controller from that storyboard, using instantiateViewControllerWithIdentifier:. This will create a table view controller object containing all your static cells
Add this as a child view controller to your original view controller, using addChildViewController:
Add the table view controller's tableView as a subview
It may be simpler to add a container view in the storyboard to hold this view, and reveal it when the button is pressed, as Mike Slutsky suggested - this will do all of the instantiating and adding and child view controller-ing work for you, but the principle is still the same.
Also, adding a table view as a subview to a table view controller sounds very dodgy - a table view controller already has a table view as its view, and you can't really add subviews to that.
The thing your missing is the association between the programatically instantiated tableview and the UITableView that you put in your storyboard. You cannot just draw UITableViews in your storyboard and start instantiating new UITableViews in the controller's code and expect xcode to know which UITableView you wanted to use from the storyboard. Use an IBOutlet to connect a global variable for the controller to the UITableView in the storyboard, then the controller will know what you're trying to refer to. If you want that UITableView to appear on a button click, simply set the UITableView to hidden by default in the storyboard and then unhide it when the button is pressed.
The thing You are missing called manual. Check this protocol for TableView dataSource https://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDataSource_Protocol/Reference/Reference.html#//apple_ref/occ/intf/UITableViewDataSource
P.S. Here good tutorial for storyboards http://maniacdev.com/ios-5-sdk-tutorial-and-guide/xcode-4-storyboard

Resources