3

for my iOS app (swift) I'm trying to add a custom UIView above the tableView. When the user pulls down the tableView, the app should look as in this screenshot.

Screenshot of current status

The text in the blue area at the top ("Zorneding ...") is still part of the tableView header. The blue area above the text is a UIView, which I've added in the ViewDidAppear function of the TableViewController.

The code I'm using for adding the UIView in ViewDidAppear is as follows.

    // Blue background view behind refresh control
    var frame = tableView.bounds
    frame.origin.y = -frame.size.height
    frame.size.width -= 10
    frame.origin.x += 5
    let refreshBackgroundView = UIView(frame: frame)
    refreshBackgroundView.backgroundColor = GlobalConstants.Color.ZeroBlue
    tableView.insertSubview(refreshBackgroundView, atIndex: 0)

This code works perfectly well in portrait orientation. But when rotating the device to landscape, the refreshBackgroundView of course does not change it's size and therefore only covers parts of the screen.

I've already tried to add constrains to the refreshBackgroundView, but I can't get them working.

The other thing I've tried is to subclass the UIRefreshControl and to configure its background there, but when pulling down the tableView, a small gap between the tableView header and the refresh control view is visible (the tableView background shines through).

Any idea of how to make my code work when the device is rotated?

Thanks!

Solution:

The solution was easy enough. Using layout constraints. I don't know why those didn't work for me yesterday.

Here's the new code:

    // Add background view behind UIRefreshControl
    let refreshBackgroundView = UIView()
    refreshBackgroundView.backgroundColor = GlobalConstants.Color.ZeroBlue
    tableView.insertSubview(refreshBackgroundView, atIndex: 0)

    refreshBackgroundView.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint(item: refreshBackgroundView, attribute: .Height, relatedBy: .Equal, toItem: tableView, attribute: .Height, multiplier: 1.0, constant: 0.0).active = true
    NSLayoutConstraint(item: refreshBackgroundView, attribute: .Width, relatedBy: .Equal, toItem: tableView, attribute: .Width, multiplier: 1.0, constant: -10.0).active = true
    NSLayoutConstraint(item: refreshBackgroundView, attribute: .Bottom, relatedBy: .Equal, toItem: tableView, attribute: .Top, multiplier: 1.0, constant: 0.0).active = true
    NSLayoutConstraint(item: refreshBackgroundView, attribute: .CenterX , relatedBy: .Equal, toItem: tableView, attribute: .CenterX, multiplier: 1.0, constant: 0.0).active = true
Brezentrager
  • 879
  • 1
  • 7
  • 9

2 Answers2

2

I would setup the refreshBackgroundView in a Xib with the rest of the interface elements.

Personally, I find Xibs are the best/most-straightforward way to setup layout constraints. And if you follow this approach it means your constraints should work fine in either landscape or portrait, without any strings attached.

Rich
  • 882
  • 1
  • 8
  • 19
1

The answer is: use AutoLayout.

Storyboard

You can add all the constraints you need from the storyboard

XIB

If loading a UIView programmatically, you can also add NSLayoutConstraint programmatically. They are guaranteed to work in landscape mode.

...but I can't get them working...

Look at https://stackoverflow.com/a/18759148/218152 and add constraints for refreshBackgroundView height. To pin it:

// Add constraints to fit to superview, below the nav bar
UIView * hintView = self.refreshBackgroundView;
[hintView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.tableView addConstraints:[NSLayoutConstraint
                               constraintsWithVisualFormat:@"H:|-0-[hintView]-0-|"
                               options:NSLayoutFormatDirectionLeadingToTrailing
                               metrics:nil
                               views:NSDictionaryOfVariableBindings(hintView)]];

id<UILayoutSupport> guide = self.topLayoutGuide;
[self.tableView addConstraints:[NSLayoutConstraint
                               constraintsWithVisualFormat:@"V:|[guide]-0-[hintView]-0-|"
                               options:NSLayoutFormatDirectionLeadingToTrailing
                               metrics:nil
                               views:NSDictionaryOfVariableBindings(hintView, guide)]];

UIRefreshControl

You should really use a UIRefreshControl for long term compatibility.

Community
  • 1
  • 1
SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
  • Thanks! I really don't know why the constraints didn't work for me yesterday. I got some weird error messages about the view hierarchy which is not yet available and that constraints therefore can't be used. Anyway, when trying to add the constraints today, everything worked perfectly well. BTW, I'm of course using the functionalities of UIRefreshControl. I just wanted to customize the visual appearance of the refresh control - and I couldn't customize it within the UIRefreshControl class in the way I wanted to. – Brezentrager Feb 17 '16 at 19:47
  • Great. Did you by any chance add `NSLayoutConstraint` programmatically, in which case the order matters? See http://stackoverflow.com/a/31578984/218152 – SwiftArchitect Feb 17 '16 at 20:49
  • You are my Hero of the Day! That's exactly what happened. I had added to constraints with `addConstraint` - and I've obviously added them to the wrong view. When re-doing the code today I used `.active = true` instead, which automatically adds them to the correct views. I should have read the UIView documentation in more detail. Thanks a million! – Brezentrager Feb 17 '16 at 21:01