Large Navigation Header transition is not smooth while scrolling - ios

I am trying to build a view controller with collection view that comes with a custom sticky segment control header. Tapping on a segment in the segmented control, will refreshes the data in the collection view and also resets the content offset to zero.
While doing this i ran into an issue with navigation bar transition from large to small and vice versa.. As you can see here, the transition is jumpy and not smooth at all..
I was able to resolve this issue by changing the below line:
edgesForExtendedLayout = []
to this:
edgesForExtendedLayout = .all
But, adding the above line is messing up my attempt to reset the content offset to zero when i tap on a segment in the segment control. As you can see below:
If its not clear in the above gif, when i tap on the segment, the collection view scrolls along with the navigation bar.. and the content offset is still a bit wrong.. I want to keep the header where it is before the segment was tapped on and only reset the collection view content offset.
I also tried to set extendedLayoutIncludesOpaqueBars to true and false but had no effect. I've also tried to play with contentInsetAdjustmentBehavior of collection view but not much luck with that as well.
Here's the code where I am setting up the view.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.title = "Sticky Section Headers"
self.edgesForExtendedLayout = .all
self.extendedLayoutIncludesOpaqueBars = true
self.navigationItem.searchController = UISearchController()
setUpNavBarAppearance()
setUpCollectionView()
configureDataSource()
}
func setUpCollectionView() {
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout())
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.backgroundColor = .systemBackground
collectionView.register(ListCell.self, forCellWithReuseIdentifier: ListCell.reuseIdentifier)
collectionView.register(SegmentedControlSupplementaryView.self,
forSupplementaryViewOfKind: sectionHeaderElementKind,
withReuseIdentifier: SegmentedControlSupplementaryView.reuseIdentifier)
self.view.addSubview(collectionView)
collectionView.refreshControl = refreshControl
self.collectionView = collectionView
}
This is what I am doing when I tap on a segment in the segment control.
#objc func segmentChanged() {
self.collectionView?.setContentOffset(.zero, animated: true)
}
I've also tried to use scrollTo methods on collection view but the result has been the same.. I've also tried to set collection view bounds to safe layout guides top and bottom anchors.. that didn't help much either..
Here is the gist file with full code for the view in case if you would like to try it.
https://gist.github.com/prasadpamidi/829e636d4697fda025bb0795ee81e355
There is not much documentation or guidelines around how to do this properly.. I was able to reproduce this with the Apple's sample app itself.
Appreciate any help with resolving this issue..

Related

UISearchController - Is there a way to avoid the top space/ padding when showing result with a new controller?

This is how I setup my UISearchController (I am using a new controller BackupSearchResultsVC to show the search result)
class BackupViewViewController: UIViewController {
private lazy var searchController: UISearchController = {
let backupSearchResultsVC = BackupSearchResultsVC.instanceFromNib()
backupSearchResultsVC.postInit(nsBackup)
let searchController = UISearchController(searchResultsController: backupSearchResultsVC)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = true
searchController.searchBar.placeholder = "search_notes".localized
searchController.searchBar.delegate = self
return searchController
}()
override func viewDidLoad() {
super.viewDidLoad()
/** Specify that this view controller determines how the search controller is presented.
The search controller should be presented modally and match the physical size of this view controller.
*/
definesPresentationContext = true
navigationItem.searchController = searchController
}
This is the UI before search begins.
Once the search begins (The search bar will move upward to block the title bar when the search text field is in focus), there is a wide gap between the search result (red background) and the search text bar.
Is there any way to avoid the gap, yet able to use a new controller to show the result?
Thanks.
By default, the presentation is full screen and the searchResultsController goes under the nav bar which has the search bar.
I believe this has something to do with the autolayout constraints / background color set up of your view controller set up to be the searchResultsController
For example, I have set up the like this:
class BackupSearchResultsVC: UIViewController, UISearchResultsUpdating
{
let resultsView = UIView()
override func viewDidLoad()
{
super.viewDidLoad()
configureResultsView()
}
private func configureResultsView()
{
resultsView.translatesAutoresizingMaskIntoConstraints = false
resultsView.backgroundColor = .red
view.addSubview(resultsView)
// Add constraints with top anchor of 200
view.addConstraints([
resultsView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
resultsView.topAnchor.constraint(equalTo: view.topAnchor, constant: 200),
resultsView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
resultsView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
Because of the top anchor, it gives me the same results you see:
If you want to keep the red view's position as it is, add a background color to your view controller's main view
override func viewDidLoad()
{
super.viewDidLoad()
// Give a background color
view.backgroundColor = .white
configureResultsView()
}
Or you could just remove the constraint to the top
// Add constraints without top anchor of 200
view.addConstraints([
resultsView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
resultsView.topAnchor.constraint(equalTo: view.topAnchor),
resultsView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
resultsView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
If this still does not work, try backupSearchResultsVC.modalPresentationStyle = .fullScreen during your set up
Give one of these a go and see if it solves your problem
Update based on Mr Cheng's
It seems giving the UIViewController used as the searchResultsController seems to make the UINavigationBar go transparent when active and the full view controller is shown behind:
This is different to what I see running the same code:
This leads me to believe this is iOS related as my device is iOS 14 and the result you see is probably iOS 15.
Digging a bit deeper, I came across this post and this which suggests by default, the bar tint is transparent on iOS 15.
So I believe you might need to set a custom background color for the nav bar in iOS 15 as outlined here perhaps

Nested scrollview with Vertical scrolling for both [duplicate]

I need to do this app that has a weird configuration.
As shown in the next image, the main view is a UIScrollView. Then inside it should have a UIPageView, and each page of the PageView should have a UITableView.
I've done all this so far. But my problem is that I want the scrolling to behave naturally.
The next is what I mean naturally. Currently when I scroll on one of the UITableViews, it scrolls the tableview (not the scrollview). But I want it to scroll the ScrollView unless the scrollview cannot scroll cause it got to its top or bottom (In that case I'd like it to scroll the tableview).
For example, let's say my scrollview is currently scrolled to the top. Then I put my finger over the tableview (of the current page being shown) and start scrolling down. I this case, I want the scrollview to scroll (no the tableview). If I keep scrolling down my scrollview and it reaches the bottom, if I remove my finger from the display and put it back over the tebleview and scroll down again, I want my tableview to scroll down now because the scrollview reached its bottom and it's not able to keep scrolling.
Do you guys have any idea about how to implement this scrolling?
I'm REALLY lost with this. Any help will be greatly appreciate it :(
Thanks!
The solution to simultaneously handling the scroll view and the table view revolves around the UIScrollViewDelegate. Therefore, have your view controller conform to that protocol:
class ViewController: UIViewController, UIScrollViewDelegate {
I’ll represent the scroll view and table view as outlets:
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var tableView: UITableView!
We’ll also need to track the height of the scroll view content as well as the screen height. You’ll see why later.
let screenHeight = UIScreen.mainScreen().bounds.height
let scrollViewContentHeight = 1200 as CGFloat
A little configuration is needed in viewDidLoad::
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSizeMake(scrollViewContentWidth, scrollViewContentHeight)
scrollView.delegate = self
tableView.delegate = self
scrollView.bounces = false
tableView.bounces = false
tableView.scrollEnabled = false
}
where I’ve turned off bouncing to keep things simple. The key settings are the delegates for the scroll view and the table view and having the table view scrolling being turned off at first.
These are necessary so that the scrollViewDidScroll: delegate method can handle reaching the bottom of the scroll view and reaching the top of the table view. Here is that method:
func scrollViewDidScroll(scrollView: UIScrollView) {
let yOffset = scrollView.contentOffset.y
if scrollView == self.scrollView {
if yOffset >= scrollViewContentHeight - screenHeight {
scrollView.scrollEnabled = false
tableView.scrollEnabled = true
}
}
if scrollView == self.tableView {
if yOffset <= 0 {
self.scrollView.scrollEnabled = true
self.tableView.scrollEnabled = false
}
}
}
What the delegate method is doing is detecting when the scroll view has reached its bottom. When that has happened the table view can be scrolled. It is also detecting when the table view reaches the top where the scroll view is re-enabled.
I created a GIF to demonstrate the results:
Modified Daniel's answer to make it more efficient and bug free.
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var tableHeight: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
//Set table height to cover entire view
//if navigation bar is not translucent, reduce navigation bar height from view height
tableHeight.constant = self.view.frame.height-64
self.tableView.isScrollEnabled = false
//no need to write following if checked in storyboard
self.scrollView.bounces = false
self.tableView.bounces = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30))
label.text = "Section 1"
label.textAlignment = .center
label.backgroundColor = .yellow
return label
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row: \(indexPath.row+1)"
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == self.scrollView {
tableView.isScrollEnabled = (self.scrollView.contentOffset.y >= 200)
}
if scrollView == self.tableView {
self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)
}
}
Complete project can be seen here:
https://gitlab.com/vineetks/TableScroll.git
After many trials and errors, this is what worked best for me. The solution has to solve two needs 1) determine who's scrolling property should be used; tableView or scrollView? 2) make sure that the tableView doesn't give authority to the scrollView until it has reached the top of it's table/content.
In order to see if the scrollview should be used for scrolling vs the tableview, i checked to see if the UIView right above my tableview was within frame. If the UIView is within frame, it's safe to say the scrollView should have authority to scroll. If the UIView is not within frame, that means that the tableView is taking up the entire window, and therefor should have authority to scroll.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.bounds.intersects(UIView.frame) == true {
//the UIView is within frame, use the UIScrollView's scrolling.
if tableView.contentOffset.y == 0 {
//tableViews content is at the top of the tableView.
tableView.isUserInteractionEnabled = false
tableView.resignFirstResponder()
print("using scrollView scroll")
} else {
//UIView is in frame, but the tableView still has more content to scroll before resigning its scrolling over to ScrollView.
tableView.isUserInteractionEnabled = true
scrollView.resignFirstResponder()
print("using tableView scroll")
}
} else {
//UIView is not in frame. Use tableViews scroll.
tableView.isUserInteractionEnabled = true
scrollView.resignFirstResponder()
print("using tableView scroll")
}
}
hope this helps someone!
None of the answers here worked perfectly for me. Each one had it's owned nuanced problem (needing to do a repeated swipe when one scrollview hit it's bottom, or the scroll indicator not looking correct, etc), so figured I'd throw in another answer.
Ole Begemann has a great write up on doing this exactly https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/
Despite being an old post, the concepts still apply to the current APIs. Additionally, there is a maintained (Xcode 9 compatible) Objective-C implementation of his approach https://github.com/eyeem/OLEContainerScrollView
If you are facing problem with the nested scrolling issue , here tis the simplest solution for it .
go to your design screen
select your scroll view and then disable bounce on scroll
if your view uses table view inside scroll view then disable bounce on scroll of the table view as well
run and check it is solved
check how to disable bounce on scroll of a scroll view
check how to disable bounce on scroll of a tableview view
I was struggling with this problem, too. There is a very simple solution.
In interface builder:
create simple ViewController
add a simple View, it will be our header, and constrain it to superview
it's the red view on the example below
I have added 12px from top, left and right, and set fixed height to 128px
embed a PageViewController, making sure it is constrained to the superview, and not the header
Now, here comes the fun part: for each page you add, make sure its tableView has an offset from top. Thats it. You can do if with this code, for example (assuming you use UITableViewController as a page):
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let tables = viewControllers.compactMap { $0 as? UITableViewController }
tables.forEach {
$0.tableView.contentInset = UIEdgeInsets(top: headerView.bounds.height, left: 0, bottom: 0, right: 0)
$0.tableView.contentOffset = CGPoint(x: 0, y: -headerView.bounds.height)
}
}
No messy scroll inside scroll inside table view, no mangling with delegates, no duplicated scrolls, perfectly natural behavior. If you can't see the header, it is probably because of the tableView background color. You have to set it to clear, for the header to be visible from under the tableView.
I think there are two options.
Since you know the size of the scroll view and the main view, you are unable to tell whether the scroll view hit the bottom or not.
if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
// reach bottom
}
So when it hit; you basically set
[contentScrollView setScrollEnabled:NO];
and other way around for your tableView.
The other thing, which is more precise I think, is to add Gesture to your views.
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(respondToTapGesture:)];
// Specify that the gesture must be a single tap
tapRecognizer.numberOfTapsRequired = 1;
// Add the tap gesture recognizer to the view
[self.view addGestureRecognizer:tapRecognizer];
// Do any additional setup after loading the view, typically from a nib
So when you add Gesture, you can simply control the active view by changing setScrollEnabled in the respondToTapGesture.
I found an awesome library
MXParallaxHeader
In Storyboard just set UIScrollView class to MXScrollView then magic happens.
I used this class to handle my UIScrollView when I embed a UIPageViewController container view. even you can insert a parallax header view for more detail.
Also, this library provides Cocoapods and Carthage
I attached an image below which represent UIViewHierarchy.
MXScrollView Hierarchy
SWIFT 5
I had some trouble using Vineet's answer for when I could not guarantee the scrollView content offset (Y) due to various different screen sizes. To resolve this, I changed the first trigger event of when the tableView's scroll gets enabled.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.bounds.contains(button.frame) {
tableView.isScrollEnabled = true
}
if scrollView == tableView {
self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)
}
}
The scrollView.bounds.contains will check if a given element's frame is FULLY within the scrollView's visible content. I set this to a button that I have below the tableView. You could set this to your tableVIew's frame instead if your only condition is that your tableView is fully visible.
I left the original implementation of when to disable the tableView's scroll and it works very well.
I tried the solution marked as the correct answer, but it was not working properly. The user need to click two times on the table view for scroll and after that I was not able to scroll the entire screen again. So I just applied the following code in viewDidLoad():
tableView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(tableViewSwiped)))
scrollView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(scrollViewSwiped)))
And the code below is the implementation of the actions:
func tableViewSwiped(){
scrollView.isScrollEnabled = false
tableView.isScrollEnabled = true
}
func scrollViewSwiped(){
scrollView.isScrollEnabled = true
tableView.isScrollEnabled = false
}
One easy trick, if you want to achieve it is replacing parent scrollview with normal container view.
Adding a pan gesture on container view, you can play with top constraint of first view to assign negative values. You can keep a check of page View's origin if it achieves to top you can start assigning that value on content offset of the pageView's child view. Until user achieves the table view in a state of top most view in container view, you can keep page tableView's scrolling disabled and allow scrolling manually by setting content offset.
So initially the page view height will be collapsed (or say out of screen) or less at bottom. Later on scrolling down it will expand to take more space.
Gesture will automatically stop responding if out of frames say on nav bar or other view outside container view.
Gestures are a key to user interactive transitions used in many apps. You can mimic scroll for a certain time with it.
In my case I'm using constraint for height like that:
self.heightTableViewConstraint.constant = self.tableView.contentSize.height
self.scrollView.contentInset.bottom = self.tableView.contentSize.height
Below code works great for me
As I wanted to show some header after some scroll and table view supposed to scroll
And in ViewDidLoad add
override func viewDidLoad() {
super.viewDidLoad()
mainScrollView.delegate = self
}
Change 265 to whatever number you want to stop upper scroll
extension AccountViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print(notebookTableView.contentOffset.y)
if notebookTableView.contentOffset.y < 265 {
if notebookTableView.contentOffset.y > 0 {
mainScrollView.setContentOffset(notebookTableView.contentOffset, animated: false)
} else {
mainScrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false)
}
} else {
mainScrollView.setContentOffset(CGPoint(x: 0.0, y: 265), animated: false)
}
}
}
CGFloat tableHeight = 0.0f;
YourArray =[response valueForKey:#"result"];
tableHeight = 0.0f;
for (int i = 0; i < [YourArray count]; i ++) {
tableHeight += [self tableView:self.aTableviewDoc heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
}
self.aTableviewDoc.frame = CGRectMake(self.aTableviewDoc.frame.origin.x, self.aTableviewDoc.frame.origin.y, self.aTableviewDoc.frame.size.width, tableHeight);
Maybe brute-force, but working perfectly if cell heights are the same: by the way, I use auto layout.
for the tableView (or collectionView or whatever), set an arbitrary height in storyboard, and make an outlet to class. Wherever appropriate, (viewDidLoad() or...) set the tableView's height big enough so that tableView doesn't need to scroll. (need to know the number of rows in advance) Then only the outer scrollView will scroll nicely.

Strange white space at UITableView header when using UISearchController with UITableViewController on iOS 10

The white space only appears on iOS 10.
I ran into this problem as well. If you have the vertical scroll indicator enabled, you should be able to see that it's a UIScrollView's inset issue. And seems like it only happens when you use a UITableViewcontroller as the searchResultsController of a UISearchController.
And this extra space is visible at both the top and bottom of the view.
This answer is not pretty, but I'm adding this in for now.
if #available(iOS 10.0, *) {
automaticallyAdjustsScrollViewInsets = false
tableView.contentInset = UIEdgeInsetsMake(64, 0, 44, 0)
}
You're setting a UITableViewController as the UISearchController's searchResultsController but without Autolayout nor a frame.
As you can read in the UISearchController's Quick Help, you can pass it nil if you want to display the search results in the same view controller that displays your searchable content.
So you're code will look okay if you set it like this:
class ViewController: UIViewController {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView(frame: view.bounds, style: .plain)
tableView.dataSource = self
view.addSubview(tableView)
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
navigationItem.titleView = searchController.searchBar
searchController.searchBar.becomeFirstResponder()
searchController.searchBar.text = "⬇️ What is this white space? ⬇️"
}
}
...
Based on #yishus answer here: https://stackoverflow.com/a/39871273/4405051
I ran into this problem as well. If you have the vertical scroll indicator enabled, you should be able to see that it's a UIScrollView's inset issue. And seems like it only happens when you use a UITableViewcontroller as the searchResultsController of a UISearchController.
And this extra space is visible at both the top and bottom of the view.
This answer is not pretty, but I'm adding this in for now.
if #available(iOS 10.0, *) {
automaticallyAdjustsScrollViewInsets = false
tableView.contentInset = UIEdgeInsetsMake(64, 0, 44, 0)
}
I disable vertical scroll indicator but the problem is still there. Also, you can't use #available to check for iOS version. It is used for checking that an API is available or not in an iOS version. So I ended up using this solution:
In search results controller (not the main view controller):
override func viewDidLoad() {
super.viewDidLoad()
if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 10,
minorVersion: 0,
patchVersion: 0)) {
automaticallyAdjustsScrollViewInsets = false
}
}
When update search results in main view controller:
func updateSearchResults(for searchController: UISearchController) {
if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 10,
minorVersion: 0,
patchVersion: 0)) {
searchResultsController.tableView.contentInset = UIEdgeInsets(top: topLayoutGuide.length,
left: 0,
bottom: bottomLayoutGuide.length,
right: 0)
}
// Filter results here
}
searchResultsController is the controller mentioned above.
If you want to handle orientation, set the searchResultsController.tableView.contentInset again when orientation changed.
Still, there's one more problem, everytime the main view controller appear (switch from another tab bar, pop a view controller,...), updateSearchResults is called. It's pretty bad for performance since I load results asynchronously.
I tried a different approach that seems to have worked for me. For your app's main target, set "Hide Status Bar" to true. At least for me (Xcode 8 GM seed) this actually did not hide the status bar within the app, but seems to have corrected the spacing issue.
Whereas this fixed the issue for me when the phone is vertically oriented, it did not entirely resolve the spacing issue when horizontally oriented. This bit of code also fixes the issue on horizontally aligned screens:
override var prefersStatusBarHidden: Bool {
get {
return UIApplication.shared.isStatusBarHidden
}
}
Of course, setting the prefersStatusBarHidden value to false will entirely disable the status bar, but that may not be a feasible workaround for some developers.
Hope this helps. This is a bit of a hack and I hope Apple resolves this in a future iOS update.
Finally, I found the easiest solution:
automaticallyAdjustsScrollViewInsets = false
🎉😃

Having multiple tableViews in a scrollview with paging makes tableHeaderView's searchBar non touchable

I have 5 tableViewControllers using UISearchResultsUpdating protocol with the new UISearchController
private var resultSearchController:UISearchController!
resultSearchController = UISearchController(searchResultsController: nil)
resultSearchController.searchResultsUpdater = self
resultSearchController.dimsBackgroundDuringPresentation = false
resultSearchController.searchBar.sizeToFit()
tableView.tableHeaderView = resultSearchController.searchBar
tableView.tableHeaderView?.backgroundColor = UIColor.redColor()
tableView.tableHeaderView?.userInteractionEnabled = true
When I load this viewController by pushing it on top of other viewControllers, everything works fine. I can touch the cells, I can touch the searchBar and operate a search.
Now, when I put 5 (or less, it doesn't matter) of those tableViewControllers inside a controller containing a scrollView
let rect: CGRect = self.view.frame
scrollView = UIScrollView(frame: rect)
scrollView.pagingEnabled = true
scrollView.addSubview(oneTableViewController)
scrollView.addSubview(twoTableViewController)...
I can navigate through the 5 tableViewController by swiping left and right.
I can select a row in any tableview.
but I can't touch the searchBar anymore...
I tried to set:
scrollView.exclusiveTouch = false
ensure that tableView.tableHeaderView?.userInteractionEnabled = true
Any ideas?
By looking at your problem I think there can be a better way to achieve what you are doing.
Instead of using 5 tableview in a scrollview, I will say use one tableview and you can set its cell to show data.. Now you can add scrollview in different cell and add your data on scrollview that will allow you to scroll left and right.
Otherwise if you wanna continue the same approach you are doing then,
you can rotate tableview to 90 degree so that it gives you a feel of left and right scrolling.
You can use your tableview'd delegate:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//do the check
if tableView == YourTableView {
//do your stuff. As you have indexPath you can get row at that indexPath
}
}
If you try to do such thing, don't forget to add the tableViewControllers as childViewControllers of your main ViewController.
self.addChildViewController(tableVC1)
tableVC1.didMoveToParentViewController(self)
self.addChildViewController(tableVC2)
tableVC2.didMoveToParentViewController(self)
now everything works fine!

Why does the first subview's position of a UIVewController embedded in a UINavigationController get altered?

I've noticed some strange behavior of UITablesViews inside a ViewController when said ViewController is embedded within a UINavigationController. The following is the code of a simple prototype for selecting different UITableViews to be shown in a scene excluding the methods for the TableView and selecting which view to show.
class ChooseTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableViewA: UITableView = UITableView()
var tableViewB: UITableView = UITableView()
var colors: [String] = ["red", "blue", "green"]
var shapes: [String] = ["triangle", "circle", "square"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableViewA.frame = CGRectMake(0, 50, 320, 200)
tableViewA.delegate = self
tableViewA.dataSource = self
tableViewA.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cellTableViewA")
tableViewB.frame = CGRectMake(0, 50, 320, 200)
tableViewB.delegate = self
tableViewB.dataSource = self
tableViewB.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cellTableViewB")
self.view.addSubview(tableViewB)
self.view.addSubview(tableViewA)
tableViewB.hidden = true
let segmentSelectorLabels = ["one", "two"]
let segmentSelector = UISegmentedControl(items: segmentSelectorLabels)
segmentSelector.frame = CGRectMake(self.view.frame.width/2 - 50, self.view.frame.height - 100, 100, 40)
self.view.addSubview(segmentSelector)
segmentSelector.selectedSegmentIndex = 0
segmentSelector.addTarget(self, action: "chooseTable:", forControlEvents: .ValueChanged)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
...
}
and here are the results of when ChooseTableViewController is not embedded in a UINavigationController and when it is
Simulators results
In another project the behavior can be fixed by simply adding
let emptyView:UIView = UIView()
and
self.view.addSubiew(emptyView)
with emptyView being the first added subview. This is a hackney solution to having the UITableViews placed in their proper position. Any insight to this behavior is appreciated.
uncheck "Adjust Scroll View Insets" for the viewcontroller in the Attributes inspector or do it in code:
self.automaticallyAdjustsScrollViewInsets = NO; (Obj-C)
self.automaticallyAdjustsScrollViewInsets = false (Swift)
some more explanation:
if this property is set to YES / true the viewcontroller - as the name says - automatically adjusts the insets for the first scrollview in its view hierarchy. this can be helpful if your scrollview / tableview / textview / webview takes up the whole screen and parts of it are normally hidden by the statusbar / navigationbar / tabbar or toolbar. then those insets make your content appear below the top bars / above the bottom bars. this though only happens when the scrollview / ... is the TOP MOST subview in the view hierarchy (= the subview at index 0). to make things clearer i uploaded four examples:
property set to YES / true and two textviews in the view hierarchy (at index 0 and 1): as you can see the insets are only set for the first scrollview
property set to NO / false and two textviews in the view hierarchy (at index 0 and 1): as you can see no insets are set at all
property set to YES / true, a button (subview at index 0) and two textviews (at index 1 and 2): as you can see no insets are set at all although the property is YES / true. that is because no scrollview is the TOP MOST subview in the view hierarchy.
property set to YES / true an a textview in the view hierarchy at index 0 taking up the whole screen: as you can see although the textview takes up the whole screen (starts at 0,0) the text is not hidden by the status- / navigationbar because the viewcontroller automatically adjusted the textview's (scrollview's) inset.
i hope i could help making things a bit clearer. :)
Set table view's frame origin y-positions to 0. Find "Extended Edges" settings for this view controller in storyboard and turn off "Under Top Bars".

Resources