36

If you look at your Inbox in iPhone OS 3.0's Mail app, you'll see that swiping down displays a grayish background color above the UISearchBar.

Now, if you scroll down to the bottom of the table, you'll see that the background color at that end is white.

I can think of a couple ways of solving this problem, but they're pretty hacky:

  • Change the table view's background color depending on the current scrollOffset by overriding -scrollViewDidScroll:
  • Give the UITableView a clear background color and then set its superview's backgroundColor to a gradient pattern image.

Does anyone know what the "best practice" solution is for this problem? thanks.

Aaron Brethorst
  • 763
  • 1
  • 6
  • 17

13 Answers13

43

There´s good answers at Light gray background in “bounce area”...

Where i found this codesnipet (slightly modified) that works great:

CGRect frame = self.tableView.bounds;
frame.origin.y = -frame.size.height;
UIView* grayView = [[UIView alloc] initWithFrame:frame];
grayView.backgroundColor = [UIColor grayColor];
[self.tableView addSubview:grayView];
[grayView release];

Swift:

var frame = self.tableView.bounds
frame.origin.y = -frame.size.height
let grayView = UIView(frame: frame)
grayView.backgroundColor = .gray
self.tableView.addSubview(grayView)
Olof
  • 5,348
  • 4
  • 25
  • 27
  • If you are also using a UIRefreshControl then you need to set the grayView as transparent like so: grayView.alpha = .5; – Daniel Apr 12 '13 at 05:37
  • 2
    This seems easier and more general than the accepted answer. – stuckj Sep 09 '14 at 15:35
  • 2
    This worked great. Here is the same thing in Swift 3: http://d.pr/n/JPkG+ – Clifton Labrum Dec 18 '16 at 00:58
  • 1
    @Daniel Another option that worked for me was increasing the zPosition of the refresh control's layer. – Harout360 May 16 '20 at 04:54
  • Another option when using with a `UIRefreshControl` would be to send the view to the bottom of the view stack `self.tableView.insertSubview(grayView, at: 0)` instead of `self.tableView.addSubview(grayView)` – craft Oct 06 '20 at 23:30
20

Swift 5.0+

Solution with an extension:

extension UITableView {

    func addTopBounceAreaView(color: UIColor = .white) {
        var frame = UIScreen.main.bounds
        frame.origin.y = -frame.size.height

        let view = UIView(frame: frame)
        view.backgroundColor = color

        self.addSubview(view)
    }
}

Usage: tableView.addTopBounceAreaView()

Alessandro Francucci
  • 1,528
  • 17
  • 25
14

The easiest and most lightweight way to solve this problem is:

  1. Set the background color of the table view to whatever you want - in your case, white.
  2. Put the search bar view inside a container view. Set the table view's header view to this container view (instead of the search bar view itself, which is probably what you were doing previously).
  3. In that container view, add another subview with frame equal to a rect like (0, -480, 320, 480), and set the background color of that subview to whatever color you want - in your case, grayish.

That should be all you need to do. I just did this myself and achieved the look I wanted, exactly the same as the Mail app. Using scrollViewDidScroll is a major waste of CPU resources, and subclassing UITableView is super messy, IMO.

danbretl
  • 644
  • 6
  • 14
13

Set the tableFooterView to a view of 0 height and width that draws way outside its bounds. An easy way is to add a big subview to it:

self.tableView.tableFooterView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
UIView *bigFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 1000)];
bigFooterView.backgroundColor = [UIColor whiteColor];
bigFooterView.opaque = YES;
[self.tableView.tableFooterView addSubview:bigFooterView];
[bigFooterView release];

adjust [UIColor whiteColor] and the width of your bigFooterView accordingly (if your tableView can go horizontal, you'll want it to be wider than 320). This way at the top you will see whatever your table view background is, and on the bottom whatever you set this view's background to.

Alex Pretzlav
  • 15,505
  • 9
  • 57
  • 55
  • Very nice hack. The zero-size footer doesn't increase the scroll bounds, but you still get the background color from the overdraw. Works great in iOS 7. For better multiple orientation and device support, I used `[[UIScreen mainScreen] bounds].size.height` instead of the `320` constant. – William Denniss Oct 25 '13 at 04:13
  • @WilliamDenniss, using the mainScreen's bounds may not always work, for instance if your table view is in a Master/Detail view on an iPad. It's probably better to set the width to the width of the table: `[[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.tableView.frame), 1000)];` or update the width in `viewDidLayoutSubviews` – Alex Pretzlav Nov 05 '13 at 18:03
  • Yeah, best solution I found for doing this on the bottom. – Alper Feb 26 '14 at 10:41
  • @alper this works on the top too if you use tableHeaderView and set the y value to -1000 – Alex Pretzlav Apr 08 '14 at 21:21
  • Other than the other answers this works with the large title using iOS 11 – SteffenK Oct 17 '17 at 21:26
  • You should use `self.view.bounds.width` for width since we have different screen sizes. – SteffenK Oct 17 '17 at 21:28
8

Courtesy of Erica Sadun:

- (void) scrollViewDidScroll: (UIScrollView *) sv
{
    float percent =  sv.contentOffset.y / sv.contentSize.height;
    percent = 0.5 + (MAX(MIN(1.0f, percent), 0.0f) / 2.0f);

    sv.backgroundColor = [UIColor colorWithRed:percent * 0.20392
                                         green:percent * 0.19607
                                          blue:percent * 0.61176 alpha: 1.0f];
}

and then here's the modified version I'm using:

- (void)scrollViewDidScroll:(UIScrollView *)sv
{
        UIColor *backgroundColor = nil;

        float percent = sv.contentOffset.y / sv.contentSize.height;
        percent = 0.5 + (MAX(MIN(1.0f, percent), 0.0f) / 2.0f);

        if (0.5f == percent)
        {
            backgroundColor = RGBCOLOR(233.0f, 235.0f, 237.0f);
        }
        else
        {
            CGFloat r = 233.0f * (1.0f - percent) + 255.0f * percent;
            CGFloat g = 235.0f * (1.0f - percent) + 255.0f * percent;
            CGFloat b = 237.0f * (1.0f - percent) + 255.0f * percent;
            backgroundColor = RGBCOLOR(r,g,b);
        }           
        sv.backgroundColor = backgroundColor;
}
Aaron Brethorst
  • 763
  • 1
  • 6
  • 17
  • I have the same problem but by running this code I don't see the expected result. Can you please explain how it works for you? – Panagiotis Korros Jul 24 '09 at 14:15
  • I found that I didn't need to adjust the percentage, I just decided on the color based on the initial percentage calculation. float percent = sv.contentOffset.y / sv.contentSize.height; if (percent < 0.5) { newBackgroundColor = [UIColor grayColor]; } else { newBackgroundColor = [UIColor whiteColor]; } self.tableView.backgroundColor = newBackgroundColor; } – ruxy Jan 11 '13 at 22:16
8

Here is the Swift 3 version:

var frame = self.tableView.bounds
frame.origin.y = -frame.size.height
let view = UIView(frame: frame)
view.backgroundColor = .gray
self.tableView.addSubview(view)
Ivan Smetanin
  • 1,999
  • 2
  • 21
  • 28
3

This might not be a "best practice," but if you really want to do it like Apple, there's a private UITableView property called tableHeaderBackgroundColor. The grayish color is #e2e7ed.

You could put something like this in the -viewDidLoad method of a UITableViewController:

UIColor *grayishColor = [UIColor colorWithRed:226/255.0
                                        green:231/255.0
                                         blue:237/255.0 alpha:1.0];
[self.tableView setValue:grayishColor forKey:@"tableHeaderBackgroundColor"];
lemnar
  • 4,063
  • 1
  • 32
  • 44
  • 1
    thanks, I just opened rdar://problem/7334912 asking Apple to publicly expose this property on UITableView. – Aaron Brethorst Oct 25 '09 at 20:34
  • @AaronBrethorst Has this been exposed since? – Moshe Jun 27 '11 at 14:15
  • No, this is still private, but there is a solution that's better than any of the existing answers. Basically, subclass `UITableView` and override `-layoutSubviews` to display a custom view in the appropriate space when the table view's `contentOffset` indicates it's being pulled down. – lemnar Jun 28 '11 at 00:13
2

I solved this problem with the use of autolayouts. The solution works on different screen sizes and with orientation change.

   self.tableView.tableFooterView = UIView();

   if let tableFooterView = self.tableView.tableFooterView {
         let bigFooterView = UIView();
         bigFooterView.backgroundColor = UIColor.white;
         bigFooterView.isOpaque = true;
         tableFooterView.addSubview(bigFooterView);

         bigFooterView.translatesAutoresizingMaskIntoConstraints = false;

         tableFooterView.addConstraint(NSLayoutConstraint(item: bigFooterView, attribute: .trailing, relatedBy: .equal, toItem: tableFooterView, attribute: .trailing, multiplier: 1, constant: 0));
         tableFooterView.addConstraint(NSLayoutConstraint(item: bigFooterView, attribute: .leading, relatedBy: .equal, toItem: tableFooterView, attribute: .leading, multiplier: 1, constant: 0));
         tableFooterView.addConstraint(NSLayoutConstraint(item: bigFooterView, attribute: .top, relatedBy: .equal, toItem: tableFooterView, attribute: .top, multiplier: 1, constant: 0));
         tableFooterView.addConstraint(NSLayoutConstraint(item: bigFooterView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 1000));
   }
1

I have expanded the answer in Light gray background in “bounce area” of a UITableView to the bottom side as well. Hope this helps :)

CGRect topFrame = self.tableView.bounds;
topFrame.origin.y = -topFrame.size.height;
UIView* topView = [[UIView alloc] initWithFrame:topFrame];
topView.backgroundColor = [UIColor grayColor]; // change to any color you want
[self.tableView addSubview:topView];

CGRect bottomFrame = self.tableView.bounds;
bottomFrame.origin.y = self.tableView.contentSize.height;
UIView* bottomView = [[UIView alloc] initWithFrame:bottomFrame];
bottomView.backgroundColor = [UIColor grayColor]; // change to any color you want
[self.tableView addSubview:bottomView];
Community
  • 1
  • 1
Kwan
  • 11
  • 3
  • Unfortunately this doesn't work if you want the color to be clear. It then goes back to that light gray background color. – Hackmodford Jun 19 '15 at 17:53
0

This is my solution:

    let topColor = UIColor.blue
    let bottomColor = UIColor.black

    self.tableView.backgroundColor = topColor
    self.tableView.tableFooterView = UIView(frame: CGRect.zero)
    let footerView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 1000))
    footerView.backgroundColor = bottomColor
    self.tableView.tableFooterView?.addSubview(footerView)
0

SwiftUI solution

 var body: some View {
        
        NavigationView {
            
            List(data, id: \.self) { data in
                Text("\(data)")
            }
            .onAppear {
                let headerView = UIView(frame: CGRect(x: 0, y: -400, width: UIScreen.main.bounds.width, height: 400.0))
                headerView.backgroundColor = .lightGray
                UITableView.appearance().addSubview(headerView)
            }
            .navigationBarTitle("Title", displayMode: .inline)
        }
    }

enter image description here


If you want a different background color below the List then add another UIView to change the backgroundView:

let backgroundView = UIView()
backgroundView.backgroundColor = .black
UITableView.appearance().backgroundView = backgroundView

enter image description here

rbaldwin
  • 4,581
  • 27
  • 38
-1

You should look into using the tableHeaderView and tableFooterView properties of the UITableView.

Nathan de Vries
  • 15,481
  • 4
  • 49
  • 55
  • That doesn't really address my question. Those two views have a fixed height. Go take a look at Mail on your iPhone. See how the background color above the UISearchBar is a different color than the background color at the bottom of the table? – Aaron Brethorst Jul 12 '09 at 18:11
-1

I think you just want to set your cell's BG Color to white, and make the table's BG color the other (gray) color. Im not sure you'd have success trying to do that with transparent cells.

Dutchie432
  • 28,798
  • 20
  • 92
  • 109