13

UIView,SegmentedController & UIContainerView in a ScrollView or something similar?

In my storyboard I have a VC containing a UIView at the top, segmented controller in the middle & 2 ContainerViewControllers at the bottom with embeded segue's to new VC's.

In the new VC's I have a TableView & a CollectionView (For example, the Instagram profile).

My issue is that I need the SegmentedController and UIView to scroll with the ContainerView(TableView/CollectionView), at the moment only the ContainerView part scrolls and the parts above that are fixed.

Now I'm guessing I probably need to put everything into a UIScrollView, so I tried that & placed all the costraints correctly. But when It came to configuring it in the Swift file, I only really now how to set the height of the scroll but oviously the height I need can vary and isn't a fixed height!

If anyone can help me here that would be fantastic, or maybe point me towards a similar question already asked here? I have already looked, but did not find anything unfortunately!

Here's an image below explaining basically what I'm after, if above I wasn't very clear..

here is an example you can see: https://github.com/Jackksun/ScrollIssue

enter image description here

Jonathan Soifer
  • 2,387
  • 5
  • 23
  • 47

3 Answers3

2

So as I understood, you want to make your scroll view receive touches outside its bounds. You can achieve this by overriding hitTest:withEvent: method on the view that is actually receiving touches.

Here is an example of what I did in my project. I subclassed UIView and redirected all touches it was receiving to the specific view. In your case, you will redirect all the touches to scroll view.

.h file:

@import UIKit.UIView;

/*!
 @class         TouchableView
 @abstract      Touchable view.
 */
@interface TouchableView : UIView

/*!
 @property      viewToReceiveTouches
 @abstract      View that receives touches instead of a given view.
 */
@property (nonatomic, weak) IBOutlet UIView *viewToReceiveTouches;

@end
.m file:

@import UIKit.UIButton;
@import UIKit.UITableView;
@import UIKit.UITextField;

#import "TouchableView.h"

@implementation TouchableView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if ( [self shouldViewReceiveTouch:self inPoint:point] )
    {
        return [super hitTest:point withEvent:event];
    }
    else if ( CGRectContainsPoint(self.bounds, point) && self.isUserInteractionEnabled )
    {
        return self.viewToReceiveTouches;
    }

    return nil;
}

- (BOOL)shouldViewReceiveTouch:(UIView *)view inPoint:(CGPoint)point
{
    if ( !CGRectContainsPoint(view.bounds, point) || !view.isUserInteractionEnabled )
    {
        return NO;
    }

    if ( [view isKindOfClass:[UIButton class]] ||
         [view isKindOfClass:[UITextField class]] ||
         [view isKindOfClass:[UITableViewCell class]] ||
         [view isKindOfClass:[UITableView class]] )

    {
        return YES;
    }

    for ( UIView *subview in view.subviews )
    {
        if ( [self shouldViewReceiveTouch:subview inPoint:[view convertPoint:point toView:subview]] )
        {
            return YES;
        }
    }

    return NO;
}

@end
  • Hi thanks for that, Im just going to put it into Swift code so I understand it better! What I'm trying to achieve is kind of like the instagram Profile, where you hae the uisegmented controller but all the view is scrollable –  Feb 02 '16 at 10:48
  • 1
    it is always better when you at least rewrite the code, not just copy-paste it :) –  Feb 02 '16 at 10:56
  • Hey, It didn't work exactly but It definetly got me on the right path! thanks to you I managed to figure it out –  Feb 03 '16 at 09:32
1

Since your height can vary, the easiest way in my opinion to do what you want is to put everything in a UICollectionView. Segue? No problem, implement didSelectRowAtIndexPath, change your datas and height and reload the UICollectionView. You can also add the UICollectionView to a simple UIView, that will allow you to put some static content on top of it.

Here is a sample app to demonstrate this : https://github.com/tirrorex/Apps/tree/master/Swift/Sampleapp

You will have to implement a dynamic height, you can find some threads on this (basically you can make a special case for the cell containing your collectionview/tableview so that this cell will be load from nib, that might help you): UICollectionView Self Sizing Cells with Auto Layout

Community
  • 1
  • 1
thibaut noah
  • 1,425
  • 2
  • 10
  • 34
  • What do you mean? put the segmented controller etc all inside a collectionView? –  Jan 26 '16 at 10:40
  • 1
    Yup that's exactly what i mean. Each component of your vc will become a specific cell of the collectionview (or tableview if you don't need too much customization). So each cell is a uiview and you add each component as a subview, that will allow you to remove and update specific elements without touching the rest. – thibaut noah Jan 26 '16 at 10:45
  • If I put a UIView into a tableViewCell, I then wouldn't be able to create IBOutlets to connect to the items in that UIVIew ( for example Labels, ImageViews...). I did try doing what you mentioned into a tableView, and I always got an error something like this: IBOutlets cannot be connected to possible repeating cells (something similar anyway) –  Jan 26 '16 at 10:54
  • And If i used a tableViewController, as the main view I wouldn't be able to hide it for example when i want to show the uiCollectionView. Because I wouldnt be able to create an outlet to place in the segmentedController code –  Jan 26 '16 at 10:56
  • 1
    What do you mean by you wouldn't be able to hide it? You don't need outlets, you can create your buttons in code. You can either implement didSelectRowAtIndexPath or put some actions for your buttons. – thibaut noah Jan 26 '16 at 11:02
  • I ave 3 buttons in the segmented controller, And when button 1 is selected I need to hide view2 & view3 –  Jan 26 '16 at 11:08
  • 1
    I don't see the problem, change your datasource and reload the tableview/collectionview accordingly, i do it for drag&drop and expand/collapse. Easy way in a tableview is to put the height of the cell you don't want atm at zero, it will hide it without having to change anything. – thibaut noah Jan 26 '16 at 11:12
  • Thanks, I'll check it out... I will also post a sample app of what I have, so you can see my issue at the moment. –  Jan 31 '16 at 00:47
  • I put some buttons because it was easier than an uisegmentedControl for the purpose of the demo – thibaut noah Jan 31 '16 at 10:47
  • https://github.com/Jackksun/ScrollIssue from that example you cann see my issue.. –  Jan 31 '16 at 15:46
1

You can set dynamic size for the scrollview content. Here what I did.

  • Define dynamicHeight to store your new TableView/CollectionView height.
  • Everytime you change the segment control, get the child controller height and update the scrollview content size.

Take a look at my code (demo here)

@IBAction func selectPage(sender: AnyObject) {

    // clear all child controller before load new controller 
    for controller in childViewControllers {

        controller.removeFromParentViewController()
    }

    for view in pageContainer.subviews {

        view.removeFromSuperview()
    }

    let segment = sender as! UISegmentedControl

    if segment.selectedSegmentIndex == 0 {

        createImageController()
    }
    else {

        createTextController()
    }

    updateScrollContentSize()
}

func updateScrollContentSize() {

    // 150 is your view and segment height
    scrollView.contentSize = CGSize(width: view.frame.size.width, height: dynamicHeight + 150)
}

func createTextController() {

    let textController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("TextController") as! TextController
    dynamicHeight = textController.getTableContentHeight()
    textController.tableView.scrollEnabled = false

    self.addChildViewController(textController)
    pageContainer.addSubview(textController.view)
    textController.view.frame = CGRectMake(0, 0, pageContainer.frame.size.width, dynamicHeight)
}

func createImageController() {

    let imageController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ImageShowController") as! ImageShowController
    dynamicHeight = imageController.getTableContentHeight()
    imageController.tableView.scrollEnabled = false

    self.addChildViewController(imageController)
    pageContainer.addSubview(imageController.view)
    imageController.view.frame = CGRectMake(0, 0, pageContainer.frame.size.width, dynamicHeight * 2)
}

Hope this can help.

truongky
  • 1,117
  • 8
  • 12
  • Hi thanks for that, I have been playing around with it for a while now. But In my tableView&CollectionView I don't know how many images they're going to be, it could be 3 or it could be 30. From you code I understand your calculating the height of the view according to how many images, and I have tried doing similar but it doesnt seem to work! (I'm using Parse to download my images) if they're is a way to do this without knowing how many images/height they're going to be, that would be excellent!! –  Jan 29 '16 at 14:04
  • add a delegate to update your tableView/collectionView when the download progress done. please see my demo, I update new code there. – truongky Jan 29 '16 at 14:30
  • I've just tried it, this is what I see( same as before) http://i.stack.imgur.com/9DHIN.png then when I click on the other tab and go back to the first one nothing appears? –  Jan 29 '16 at 14:54
  • Did my demo work well when you download it? If you want, we can have a conversation via skype: nguyentruongky3390. Hope I can help. – truongky Jan 29 '16 at 15:16
  • The demo works great yes, but doesn't seem to work when your downloading the images..Or when It doesnt already have the height calculated...Shall I set a demo project connected to parse? –  Jan 29 '16 at 15:26
  • Do the images load? Even 1 image? Maybe you're querying code isn't good. How are you storing the images that you're querying from PRse? It should be in an array and then you just put them on the UIImageView based on the indexPath.row. And you set the numberOfRows to the # of array elements you have (the number of pics).... @Jack – Lukesivi Jan 30 '16 at 21:48
  • @lukesIvi hey again, my querying is working perfectly, the problem is the collection isn't resizing to to the table..not sure if thats clear...but if you check the snap shots i sent you before ( i.stack.imgur.com/q3dSJ.png i.stack.imgur.com/gFBqt.png) you'll see the problem –  Jan 30 '16 at 22:19
  • https://github.com/Jackksun/ScrollIssue from that example you cann see my issue.. –  Jan 31 '16 at 15:46
  • @Jack: please checkout my demo again. I have an update for you. Help this can help. – truongky Feb 01 '16 at 03:25