3

I have DiscountListTableViewController that is shown as a separate screen in my app. But there's another screen (PlaceDetailsViewController) where I need to show related discounts at the bottom.

Currently, PlaceDetailsViewController.view has UIScrollView as a container and I'm adding DiscountListTableViewController.tableView to the this UIScrollView.content container in viewDidLoad of PlaceDetailsViewController. This works and the table view is shown correctly, however unable to receive cell clicks.

I know UITableView inherits from UIScrollView and it's somehow not advised (but not restricted). However, from loose coupling point of view, every component should be designed in a way it could be independently used elsewhere, and it's DiscountListTableViewController in my case.

PlaceDetailsViewController component just needs DiscountListTableViewController as-is, so there's no logic reason why it can't be used directly. Any suggestions?

Steph Sharp
  • 11,462
  • 5
  • 44
  • 81
Centurion
  • 14,106
  • 31
  • 105
  • 197
  • What the reason to put UITableView into UIScrollView if UITableView is UIScrollView? – Valeriy Van Jun 15 '13 at 08:15
  • Reason in written in the question – Centurion Jun 15 '13 at 08:21
  • Do the table views actually need to **scroll**, or are all rows always visible, and you just need to be able to **select** each row? – Nate Jun 15 '13 at 08:24
  • 1
    @Nate No it do not need to scroll and table view is always visible because I'm resizing tableView with respect to its content. – Centurion Jun 15 '13 at 08:29
  • It might help you - http://stackoverflow.com/questions/3422915/scrolling-a-uitableview-inside-a-uiscrollview/17122782#17122782 – TheTiger Jun 15 '13 at 10:49

6 Answers6

27

Answer : Don't do this.

UITableview is inherited from ----> UIScrollView : UIView : UIResponder : NSObject

Apple says :

Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.

Bhavin
  • 27,155
  • 11
  • 55
  • 94
  • 3
    I know this question is super old. But what if I disable touch events and scrolling for the tableview? I just want it to layout static non user data? – NSGangster Apr 30 '16 at 20:30
  • 4
    @NSGangster that will be fine. You can just disable scrolling of tableview and put it inside scrollview. But you will have to calculate height of tableview yourself. And also it will disable cell reusing feature of tableview. – kientux Feb 21 '17 at 04:53
7

Not advised ? It kind of felt like "dont do" after reading this.The unpredictability is never a good behaviour for the app

Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.

Lithu T.V
  • 19,955
  • 12
  • 56
  • 101
  • 2
    I do completely agree with you – Prateek Prem Jun 15 '13 at 08:16
  • Actual implementations causing unpredictability should not always considered as best implementations. So, from louse coupling point of view I would say, there's a place for enchantments in UIScrollView, UITableView and UIWebView. Meanwhile, I would consider small workaround, like implementing hitTest:withEvent: – Centurion Jun 15 '13 at 08:19
  • @Lithu : I too agree but what if we want to do then ? I want a tableview inside scrollview and the tableview will not be static :) – Purushottam Sain Mar 07 '17 at 11:51
4

Update for 2020, swift 5.X that allows your tableview inside a scrollview!

  1. Create a subclass of UITableView:

     import UIKit
    
     final class ContentSizedTableView: UITableView {
         override var contentSize:CGSize {
             didSet {
                 invalidateIntrinsicContentSize()
             }
         }
    
         override var intrinsicContentSize:CGSize {
             layoutIfNeeded()
             return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
         }
     }
    
  2. Add a UITableView to your layout and set constraints on all sides. Set the class of it to ContentSizedTableView.

  3. You should see some errors, because Storyboard doesn't take our subclass' intrinsicContentSize into account. At runtime it will use the override in our ContentSizedTableView class

Maple
  • 741
  • 13
  • 28
T D Nguyen
  • 7,054
  • 4
  • 51
  • 71
  • Regarding point 3, to avoid seeing errors you can set a random height constraint in the Storyboard and make it a placeholder constraint (tick option `Remove at build time` in Storyboard's constraint Inspector panel. Also, remember to uncheck the Scrolling enabled option of the tableview when used like this. Scrolling happens already in the Scrollview in which the tableview is manually embedded. – Samuël May 12 '20 at 12:39
2

Will share how solved that. Did subclassed UIScrollView and used as container view in PlaceDetailsViewController:

@interface PlaceDetailsContainerUIScrollView : UIScrollView

@end

@implementation PlaceDetailsContainerUIScrollView

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
  UIView *result = [super hitTest:point withEvent:event];

  if ([result isKindOfClass:[UIView class]] && (result.tag == kDiscountListTableViewCellTag)
  {
    UITableView *tableView = (UITableView *) [[result.superview superview] superview];

    return tableView;

  }

  return result;
}
@end

Also, don't forget to set PlaceDetailsContainerUIScrollView.delaysContentTouches = YES and PlaceDetailsContainerUIScrollView.canCancelContentTouches = NO.

Also, needed small fix in DiscountListTableViewController method:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  DiscountDetailsViewController *discountDetailsVC = [[DiscountDetailsViewController alloc] init];

  //[self.navigationController pushViewController:discountDetailsVC animated:YES];   
  // self.navigationController is nill because it's not pushed on nav stack, so will grab the current one:
  AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

  [appDelegate.navigationController pushViewController:discountDetailsVC animated:YES];
}
Centurion
  • 14,106
  • 31
  • 105
  • 197
0

Perhaps this is late, but I managed to do this by:

  • Disable Scroll on the UITableView
  • Set the content size of the UIScrollView programmatically after I calculate the height I need
whd.nsr
  • 684
  • 6
  • 12
0

should not do this because UITableView is a subclass of UIScrollView and according to apple docs. just use auto layout more easier than you calculate every item size and stoping table scroll to do so.

apple Docs:

You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.

eng mohamed emam
  • 549
  • 6
  • 13