2
votes

I have 3 UI elements in the contentView of the UITableViewCell that need to be aligned properly when the device rotates. Currently, I enforce the frames of these UI Elements like this:

segmentedControl1.frame = CGRectMake(165, 15, 130, 40);
segmentedControl2.frame = CGRectMake(435, 15, 130, 40);
segmentedControl3.frame = CGRectMake(710, 15, 130, 40);

I would like to know how I can use the autoresizingMask property of these elements to make them resize and not-overlap on one another when the device is rotated from Landscape to portrait (default is portrait, iPad only). I do not want to have custom frame positions for each orientation.

I tried something like this:

segmentedControl1.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;
segmentedControl2.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
segmentedControl3.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;

But this doesn't seem to work. The elements are all overlapped and unhappily laid out.

Any suggestions?

UPDATE 1

Here's what the table view cell currently looks like in landscape mode:

--------------------------------------------------------------------------
|    ============              ============             ============     |
|   | A   |  B   |            | A   |  B   |           | A   |  B   |    |
|    ============              ============             ============     |
--------------------------------------------------------------------------

And here's what I want when the device is rotated to a portrait mode:

-------------------------------------------------------
|    ============    ============    ============     |
|   | A   |  B   |  | A   |  B   |  | A   |  B   |    |
|    ============    ============    ============     |
-------------------------------------------------------

UPDATE 2 Here's my code in cellForRowAtIndexPath incorporating suggestions from @Wolfert

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) 
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;

    // Configure the cell...
    segmentedControl1.frame = CGRectMake(165, 15, 180, 40);
    segmentedControl1.tag = indexPath.row;
    segmentedControl1.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
    [cell.contentView addSubview:segmentedControl1];

    segmentedControl2.frame = CGRectMake(435, 15, 180, 40);
    segmentedControl2.tag = indexPath.row;
    segmentedControl2.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
    [cell.contentView addSubview:segmentedControl2];

    segmentedControl3.frame = CGRectMake(710, 15, 180, 40);
    segmentedControl3.tag = indexPath.row;
    segmentedControl3.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
    [cell.contentView addSubview:segmentedControl3];

    return cell;
}
2

2 Answers

3
votes

This should do the trick:

segmentedControl1.autoresizingMask =   UIViewAutoresizingFlexibleRightMargin;
segmentedControl2.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
segmentedControl3.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;

Note that their super view should have this property:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
1
votes

I am a bit confused that you say you have a working landscape configuration. I would have said that combining your hardcoded CGRectMake numbers with the autoresizingMask values you cite already result in a skewed layout.

Be that as it may, here is my advice: You should not use hardcoded numbers for the width of your initial frames. Instead you should base your calculations on cell.contentView.bounds.size.width. In your case, something like this:

- (UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
  static NSString* cellIdentifier = @"Cell";
  UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  if (cell == nil) 
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];

  NSArray* items = [NSArray arrayWithObjects:@"A", @"B", nil];
  UISegmentedControl* segmentedControl1 = [[[UISegmentedControl alloc] initWithItems:items] autorelease];
  UISegmentedControl* segmentedControl2 = [[[UISegmentedControl alloc] initWithItems:items] autorelease];
  UISegmentedControl* segmentedControl3 = [[[UISegmentedControl alloc] initWithItems:items] autorelease];
  [cell.contentView addSubview:segmentedControl1];
  [cell.contentView addSubview:segmentedControl2];
  [cell.contentView addSubview:segmentedControl3];

  // Some values I like
  CGFloat marginHorizontal = 10;
  CGFloat spacingHorizontal = 8;
  // This is the crucial line!
  CGFloat totalWidth = cell.contentView.bounds.size.width;
  // That's how much is available to the three controls 
  CGFloat availableWidth = totalWidth - 2 * marginHorizontal - 2 * spacingHorizontal;
  // That's how much each control gets - initially!
  CGFloat segmentedControlWidth = availableWidth / 3.0;

  // These are the numbers from your example. With these numbers your table view
  // delegate probably implements tableView:heightForRowAtIndexPath:()
  CGFloat marginVertical = 15;
  CGFloat segmentedControlHeight = 40;

  CGRect segmentedControlFrame = CGRectMake(marginHorizontal,
                                            marginVertical,
                                            segmentedControlWidth,
                                            segmentedControlHeight);
  segmentedControl1.frame = segmentedControlFrame;
  segmentedControlFrame.origin.x += spacingHorizontal + segmentedControlWidth;
  segmentedControl2.frame = segmentedControlFrame;
  segmentedControlFrame.origin.x += spacingHorizontal + segmentedControlWidth;
  segmentedControl3.frame = segmentedControlFrame;

  // These are the values you cited initially, they are fine
  segmentedControl1.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;
  segmentedControl2.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  segmentedControl3.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;

  return cell;
}

I tried this on the iPad simulator and it worked just fine. Note that you do not have to change the autoresizingMask of either the cell or the content view. I know that this is different from how UIView normally works, but I don't have an explanation except that table view cells are strange beasts :-)

Back to your original code: The root problem there is that the hardcoded numbers assume that the content view has the full width of the screen, when in fact its initial width is much less (in my environment cell.contentView.bounds.size.width initially is 320). Thus, the initial layout looks something like this:

+-content view------------+
|    ============         |    ============             ============ 
|   | A   |  B   |        |   | A   |  B   |           | A   |  B   |
|    ============         |    ============             ============ 
+-------------------------+

As you can see, the layout is out of proportion to the initial width of the content view. When the content view is later resized to its correct width, the controls are also resized in proportion to their initial width and horizontal distribution. This causes even more out-of-proportien'ness :-)