【问题标题】:Changing a view programmatically to align left or right以编程方式更改视图以左对齐或右对齐
【发布时间】:2016-07-17 18:21:24
【问题描述】:

我一直在构建这个聊天,但我陷入了一些我还无法弄清楚的事情。

我一直在尝试根据场景将同一视图左对齐或右对齐,但我还没有得到想要的结果。

基本上,我在界面生成器中构建单元格并固定在左侧,并根据里面标签的内容使其宽度和高度可变。

我一直在尝试做的是重复使用同一个单元格并在我是发送消息的人时向右对齐(黄色),因为默认情况下,当其他人向我发送消息时它会向左对齐(灰色) 请参见下图进行说明:

我想要的是:

其他消息--------

-------------我的留言

为了实现这一点,在 TroyT 的建议下,我根据需要激活和停用了前导/尾随约束。

所以我做的是。

  1. 在我的 UITableViewCell 类中,我为我的两个约束创建了两个 @IBoulets,如下所示:

    @IBOutlet var bubbleViewLeading: NSLayoutConstraint!

    @IBOutlet var bubbleViewTrailing: NSLayoutConstraint!

  2. 稍后,在我的 tableViewController 中的方法 cellForRowAtIndexPAth 中,我根据需要执行以下操作激活或停用其中一个约束。

cell.bubbleViewTrailing.active = true cell.bubbleViewLeading.active = false

或相反

cell.bubbleViewTrailing.active = false cell.bubbleViewLeading.active = true

将设置为“True”的约束设置为 0。正如我之前提到的,这适用于除第一个以外的所有单元格。

  1. 这部分有效,因为由于某种原因,它不会影响第一行并且两个约束都保持激活状态,因此该行被拉伸到 tableview 的宽度,如下所示:。

我尝试了几种方法,例如:

  • 使用我的 UITableViewCell 类中的 init 方法来更改约束
  • 在我的 UITableViewCell 中更改 awakeFromNib 方法的约束
  • 使用情节提要左起第三个选项卡上的“用户定义的运行时属性”,添加一个名为“active”的键路径,将类型设置为 Bool,并将值设置为 false。
  • 当我在情节提要中选择约束时,在属性检查器中取消选中“已安装”

但是,这些方法都不会影响第一个单元格的约束,但我可以轻松更改其他所有内容,例如单元格背景或单元格内标签中的文本对齐方式。这只是我第一次使用单元格时不会改变的约束。即使我向下滚动到屏幕边界以“强制”重复使用单元格时,第一个单元格也会变得固定:

这几天我一直在尝试解决这个问题,但收效甚微。因此,我决定悬赏这个问题。

【问题讨论】:

  • 您想根据发送者和接收者对齐聊天单元格。右对齐的发送方单元格和使用约束左对齐的接收方单元格,对吗?
  • keshav vishwkarma,这是对的,除了第一个单元格之外,我做得很好
  • 你是如何处理约束的?我的意思是使用视图的活动属性来激活/停用约束或删除/添加约束
  • 在情节提要中,我最初创建了两个从气泡视图到单元格边距的约束。我从一个自定义单元格 .xib 创建了它,在该单元格中我将前导约束和尾随约束都设置为 0。所以结果是第一个单元格中发生的情况,正如您在屏幕截图中看到的那样。但是,我认为通过将约束 active 属性设置为 true 或 false 会给我想要的结果。但是,这部分正确,因为它不适用于我的表格视图中出现的第一个单元格
  • 我已经为您实现了 ChatMessageCell,请查看下面的答案,希望对您有所帮助。

标签: swift interface-builder ios9 xcode7 nslayoutconstraint


【解决方案1】:

你解释的对我来说意义不大,但如果它只发生在第一行,那么我会尝试强制布局重新绘制:

cell.setNeedsLayout() cell.layoutIfNeeded()

来自cellForRowAtIndexPath

顺便说一句,我认为更好的方法是注册 2 个不同的单元,并在 IB 中创建它们。这种方法将使您的代码更短,并且不仅允许您修改尾随/前导,还允许您修改颜色、字体或您需要的任何内容。你给他们 2 个不同的 ID,但保持相同的实现类,然后你只需要在你的 cellForRowAtIndexPath 上将你需要的一个 deque。

为每种样式保留不同的单元格是使用 IB 执行这些操作的标准方法。

【讨论】:

  • 我之前尝试过这些cell.setNeedsLayout() cell.layoutIfNeeded(),但对我不起作用。我也尝试了 2 单元格方法,但出现错误,但您的评论让我意识到我做错了什么(我在 tableView.registerNib 中使用了不同的 xib,但出队使用了相同的 id)。谢谢!!将为像我这样的未来新手更新我的问题
【解决方案2】:

removeConstraint: 已弃用,取而代之的是使用active 属性(或NSLayoutConstraintdeactivateConstraints:)。虽然removeConstraint: 通常仍然有效,但将其与设置约束的active 属性相结合会使情况变得更糟。要么使用 all activate 和 deactivate(首选),要么使用 add 和 remove。

所以不要使用cell.removeConstraint:,而是使用这个:

cell.bubbleViewTrailing.active = false

我可能要注意的一件事是,您的前导和尾随是相反的。前导在左边,尾随在右边,除了从右到左的定位。

【讨论】:

  • 您的观察似乎准确,但仍然有同样的问题,我会尝试看看还会发生什么,我确实纠正了我之前遇到的颠倒的前导/尾随混淆:NSLayoutConstraint(item: cell.bubbleView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.TrailingMargin, multiplier: 1.0, constant: 0.0).active = true cell.bubbleViewLeading.active = false
  • 您的回答正确地引导了我,您介意检查我更新的问题吗?除了在第一个单元格中运行良好,Interface builder 和 prepareForReuse 之间是否有任何单元格准备?
  • 尝试在init(style:reuseIdentifier:) 中调用它并停用约束(确保先调用super.init(style:reuseIdentifier:)!)。保留 prepareForReuse 实现,因为 init 将在创建时被调用,prepareForReuse 将被调用用于回收的。
  • 试过了,但仍然无法修改第一个单元格中的约束。但是,我可以修改其他所有内容,除了约束,我可以修改它的颜色、文本对齐方式。第一个单元格中的那些限制是顽固的!我认为也许我创建的网点是错误的,但事实并非如此,因为从第二个单元开始,它们运行良好
  • @Granola 为什么不直接在cellForRowAtIndexPath 中停用它?
【解决方案3】:

这是我为您实现的自定义ChatMessageCell,在其中我使用KVConstraintExtensionsMaster 库以编程方式应用了约束,以应用我已经实现的约束。

我已经更新了前导和尾随约束常量值,而不是激活/停用约束或删除/添加约束。

希望对你有帮助。

将以下代码放入ChatMessageCell.h头文件中

typedef NS_ENUM(NSUInteger, Type) {
    TypeSender,
    TypeReceiver,
};

@interface ChatMessageCell : UITableViewCell

@property (assign, nonatomic) Type cellType;
@property (strong, nonatomic) UIView *msgBackgroundView;
@property (strong, nonatomic) UILabel *messageLabel;

@end

将以下代码放入ChatMessageCell.m文件中

#import "ChatMessageCell.h"
#import "KVConstraintExtensionsMaster.h"

@implementation ChatMessageCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self createAndConfigureViewHierarchy];
    }    
    return self;
}

- (void)createAndConfigureViewHierarchy
{
    _msgBackgroundView = [UIView prepareNewViewForAutoLayout];
    [self.contentView addSubview:_msgBackgroundView];

    _msgBackgroundView.backgroundColor = [UIColor clearColor];
    [_msgBackgroundView.layer setCornerRadius:6.0];

    _labelMessage = [UILabel prepareNewViewForAutoLayout];
    [_labelMessage setLineBreakMode:NSLineBreakByTruncatingTail];
    [_labelMessage setNumberOfLines:0];
    [_labelMessage setTextAlignment:NSTextAlignmentLeft];
    [_labelMessage setTextColor:[UIColor whiteColor]];
    self.labelMessage.backgroundColor = [UIColor clearColor];

    [_msgBackgroundView addSubview:_labelMessage];

    [self applyConstraints];
}

-(void)applyConstraints
{
    // now applying the constraints by using KVConstraintExtensionsMaster library
    CGFloat padding = 8.0;

    // adding Top and Bottom contraints of _msgBackgroundView
    [_msgBackgroundView applyTopAndBottomPinConstraintToSuperviewWithPadding:padding];

    // adding leading and trailing contraints of _msgBackgroundView
    [_msgBackgroundView applyLeadingAndTrailingPinConstraintToSuperviewWithPadding:padding];

    // adding Top and Bottom contraints of _msgBackgroundView
    [_labelMessage applyTopAndBottomPinConstraintToSuperviewWithPadding:padding];

    // adding leading and trailing contraints of _msgBackgroundView
    [_labelMessage applyLeadingAndTrailingPinConstraintToSuperviewWithPadding:padding];

}

-(void)setCellType:(Type)cellType
{
    switch (cellType) {
        case TypeSender:
        {
            [_msgBackgroundView setBackgroundColor:[UIColor redColor]];
            [_labelMessage setTextAlignment:NSTextAlignmentRight];

            // this method will change the Leading Pin Constraint Constant value by 100.0
            [_msgBackgroundView applyLeadingPinConstraintToSuperviewWithPadding:100.0];

            // this method will increase the Leading Pin Constraint Constant value with proper ratio only iPad
            [_msgBackgroundView updateAppliedConstraintConstantValueForIpadByAttribute:NSLayoutAttributeLeading];
            break;
        }

        case TypeReceiver:
        {
            [_msgBackgroundView setBackgroundColor:[UIColor redColor]];
            [_labelMessage setTextAlignment:NSTextAlignmentLeft];

            // this method will change the Trailing Pin Constraint Constant value by 100.0
            [_msgBackgroundView applyTrailingPinConstraintToSuperviewWithPadding:100.0];

            // this method will increase the Leading Pin Constraint Constant value with proper ratio only iPad
            [_msgBackgroundView updateAppliedConstraintConstantValueForIpadByAttribute:NSLayoutAttributeTrailing];
            break;
        }
    }

    [self.contentView setNeedsLayout];
    [self.contentView updateModifyConstraints];
}

-(void)prepareForReuse
{
    // this method will change the Leading And Trailing Pin Constraints Constant value by 8.0
    [_msgBackgroundView applyLeadingAndTrailingPinConstraintToSuperviewWithPadding:8.0];
    [super prepareForReuse];
}

@end

把下面的代码放在你的ViewController的viewDidLoad方法中是:

[self.tableView registerClass:ChatMessageCell.class forCellReuseIdentifier:@"KVChatMessageCell"];
self.tableView.rowHeight = UITableViewAutomaticDimension;
/* any estimated height but must be more than 2 */
self.tableView.estimatedRowHeight = 44.0;

现在在你的 ViewController 中实现 UITableView 数据源

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section{
    return messages.count;
}

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

    ChatMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    [cell.labelMessage setText:messages[indexPath.row]];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    if (indexPath.row%2 == 0) {
       // here all even cell are the sender type
        [cell setCellType:TypeSender];
    }
    else {
       // here all odd cell are the Receiver type
        [cell setCellType:TypeReceiver];
    }

    return cell;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多