【问题标题】:iOS - Resize vertical views with separator viewiOS - 使用分隔视图调整垂直视图的大小
【发布时间】:2016-03-03 14:43:39
【问题描述】:

这个问题和我问here的问题很相似。 Rob 回答了我,他的代码运行良好。我正在尝试做同样的事情,但使视图垂直。

这是我现在拥有的。

我是viewDidLoad

UIView *previousContentView = nil;

    for (NSInteger i = 0; i < 4; i++) {
        UIView *contentView = [self addRandomColoredView];
        [self.view.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor].active = true;
        [self.view.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor].active = true;
        if (previousContentView) {
            VerticalSeparatorView *view = [[VerticalSeparatorView alloc] init];
            [view addSeparatorBetweenView:previousContentView secondView:contentView];
            NSLayoutConstraint *width = [contentView.widthAnchor constraintEqualToAnchor:previousContentView.widthAnchor];
            width.priority = 250;
            width.active = true;
        } else {
            [self.view.leftAnchor constraintEqualToAnchor:contentView.leftAnchor].active = true;
        }
        previousContentView = contentView;
    }
    [self.view.rightAnchor constraintEqualToAnchor:previousContentView.rightAnchor].active = true;

在我的VerticalSeparatorView:

@implementation VerticalSeparatorView

#pragma mark - Configuration

/** Add a separator between views

 This creates the separator view; adds it to the view hierarchy; adds the constraint for height;
 adds the constraints for leading/trailing with respect to its superview; and adds the constraints
 the relation to the views above and below

 @param firstView  The UIView above the separator
 @param secondView The UIView below the separator
 @returns          The separator UIView
 */

- (instancetype)addSeparatorBetweenView:(UIView *)firstView secondView:(UIView *)secondView {
    VerticalSeparatorView *separator = [[VerticalSeparatorView alloc] init];
    [firstView.superview addSubview:separator];
    separator.firstView = firstView;
    separator.secondView = secondView;

    [NSLayoutConstraint activateConstraints:@[
                                              [separator.widthAnchor constraintEqualToConstant:kTotalWidth],
                                              [separator.superview.leadingAnchor constraintEqualToAnchor:separator.leadingAnchor],
                                              [separator.superview.trailingAnchor constraintEqualToAnchor:separator.trailingAnchor],
                                              [firstView.rightAnchor constraintEqualToAnchor:separator.leftAnchor constant:kMargin],
                                              [secondView.leftAnchor constraintEqualToAnchor:separator.rightAnchor constant:-kMargin],
                                              ]];

    separator.leftConstraint = [separator.leftAnchor constraintEqualToAnchor:separator.superview.leftAnchor constant:0]; // it doesn't matter what the constant is, because it hasn't been enabled

    return separator;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        self.translatesAutoresizingMaskIntoConstraints = false;
        self.userInteractionEnabled = true;
        self.backgroundColor = [UIColor redColor];
    }
    return self;
}

#pragma mark - Handle Touches

// When it first receives touches, save (a) where the view currently is; and (b) where the touch started

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.oldX = self.frame.origin.x;
    self.firstTouch = [[touches anyObject] locationInView:self.superview];
    self.leftConstraint.constant = self.oldX;
    self.leftConstraint.active = true;
}

// When user drags finger, figure out what the new top constraint should be

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];

    // for more responsive UX, use predicted touches, if possible

    if ([UIEvent instancesRespondToSelector:@selector(predictedTouchesForTouch:)]) {
        UITouch *predictedTouch = [[event predictedTouchesForTouch:touch] lastObject];
        if (predictedTouch) {
            [self updateTopConstraintOnBasisOfTouch:predictedTouch];
            return;
        }
    }

    // if no predicted touch found, just use the touch provided

    [self updateTopConstraintOnBasisOfTouch:touch];
}

// When touches are done, reset constraint on the basis of the final touch,
// (backing out any adjustment previously done with predicted touches, if any).

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self updateTopConstraintOnBasisOfTouch:[touches anyObject]];
}

/** Update top constraint of the separator view on the basis of a touch.

 This updates the top constraint of the horizontal separator (which moves the visible separator).
 Please note that this uses properties populated in touchesBegan, notably the `oldY` (where the
 separator was before the touches began) and `firstTouch` (where these touches began).

 @param touch    The touch that dictates to where the separator should be moved.
 */
- (void)updateTopConstraintOnBasisOfTouch:(UITouch *)touch {
    // calculate where separator should be moved to

    CGFloat x = self.oldX + [touch locationInView:self.superview].y - self.firstTouch.y;

    // make sure the views above and below are not too small

    x = MAX(x, self.firstView.frame.origin.x + kMinWidth - kMargin);
    x = MIN(x, self.secondView.frame.origin.x + self.secondView.frame.size.width - (kMargin + kMinWidth));

    // set constraint

    self.leftConstraint.constant = x;
}

#pragma mark - Drawing

- (void)drawRect:(CGRect)rect
{
    CGRect separatorRect = CGRectMake(kMargin, 0, kVisibleWidth, self.bounds.size.height);
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:separatorRect];
    [[UIColor blackColor] set];
    [path stroke];
    [path fill];
}

@end

我将 contentView 右锚点连接到分隔符左锚点,并将 contentView 左锚点连接到分隔符右锚点,但运行此代码后没有显示任何内容。我做错了什么?

【问题讨论】:

    标签: ios objective-c uiview


    【解决方案1】:

    几个观察:

    1. for 循环中的约束是为水平分隔符编写的,在处理垂直分隔符时都是向后的。您必须用前导/尾随替换所有出现的顶部/底部锚点,反之亦然。您已经(大部分)在 VerticalSeparatorClass 中完成了此操作,而不是在您创建内容视图的位置。

      UIView *previousContentView = nil;
      
      for (NSInteger i = 0; i < 4; i++) {
          UIView *contentView = [self addRandomColoredView];
          [self.view.topAnchor constraintEqualToAnchor:contentView.topAnchor].active = true;
          [self.view.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor].active = true;
          if (previousContentView) {
              [VerticalSeparatorView addSeparatorBetweenView:previousContentView secondView:contentView];
              NSLayoutConstraint *width = [contentView.widthAnchor constraintEqualToAnchor:previousContentView.widthAnchor];
              width.priority = 250;
              width.active = true;
          } else {
              [self.view.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor].active = true;
          }
          previousContentView = contentView;
      }
      [self.view.trailingAnchor constraintEqualToAnchor:previousContentView.trailingAnchor].active = true;
      
    2. 另外,我不清楚您为什么将addSeparatorBetweenView 设为实例方法而不是像以前那样的类方法,因为现在您要处理两个单独的实例。这不是问题,但效率低下。

      另外,我认为应该在顶部/底部和前导/尾随之间翻转的一些约束滑过。你想要:

      + (instancetype)addSeparatorBetweenView:(UIView *)firstView secondView:(UIView *)secondView {
          VerticalSeparatorView *separator = [[VerticalSeparatorView alloc] init];
          [firstView.superview addSubview:separator];
          separator.firstView = firstView;
          separator.secondView = secondView;
      
          [NSLayoutConstraint activateConstraints:@[
              [separator.widthAnchor constraintEqualToConstant:kTotalWidth],
              [separator.superview.topAnchor constraintEqualToAnchor:separator.topAnchor],
              [separator.superview.bottomAnchor constraintEqualToAnchor:separator.bottomAnchor],
              [firstView.rightAnchor constraintEqualToAnchor:separator.leftAnchor constant:kMargin],
              [secondView.leftAnchor constraintEqualToAnchor:separator.rightAnchor constant:-kMargin],
          ]];
      
          separator.leftConstraint = [separator.leftAnchor constraintEqualToAnchor:separator.superview.leftAnchor constant:0]; // it doesn't matter what the constant is, because it hasn't been enabled
      
          return separator;
      }
      
    3. 在触摸处理代码中,您仍然引用 y,而您现在想要引用 x

      - (void)updateTopConstraintOnBasisOfTouch:(UITouch *)touch {
          // calculate where separator should be moved to
      
          CGFloat x = self.oldX + [touch locationInView:self.superview].x - self.firstTouch.x;
      
          // make sure the views above and below are not too small
      
          x = MAX(x, self.firstView.frame.origin.x + kMinWidth - kMargin);
          x = MIN(x, self.secondView.frame.origin.x + self.secondView.frame.size.width - (kMargin + kMinWidth));
      
          // set constraint
      
          self.leftConstraint.constant = x;
      }
      

    【讨论】:

    • 嗨,罗伯。而不是 contentView 是 UIView 我用 UIScrollView 替换它,因为我想放大/缩小里面的照片。我在滚动视图中有一个带有自动布局的 UIImageView,它看起来很棒。但不知何故,我可以滚动而不是捏放大/缩小滚动视图中的图像。是不是因为分隔符触摸混淆了滚动视图触摸的应用程序?
    • 如果没有别的,你可能应该减少kTotalHeightkTotalWidth 一点,以避免在你真正想要捏缩放时意外抓住分隔符。您可能还可以让分隔符查看触摸次数,并且仅在有单次触摸时才对其进行操作。但在你解决这个问题之前,请确保分隔符的触摸处理确实是问题的根源,并且图像视图缩放没有其他问题。
    • 嗨,罗伯。我认为问题不是来自分隔符,而是来自滚动视图或滚动视图约束。我用谷歌搜索发现here,如果我使用带有自动布局的滚动视图,我需要将图像视图约束与滚动视图超级视图约束连接起来。不幸的是,当我这样做时,滚动视图根本没有滚动,并且当分隔符移动时,imageView 会调整大小。
    • 我曾尝试寻找答案,但没有成功。我知道我已经要求你回答我的两个问题,但我只是不知道该问谁。如果你有时间可以看看我的问题here
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多