NSAttributedString带有图像附件和NSTextTab,文本未对齐

8
我正在尝试使用NSAttributedString实现一个带有图像和标题的UIlabel,其位于左侧,右侧是一列带有项目符号的描述。
NSMutableParagraphStyle *pStyle = [[NSMutableParagraphStyle alloc] init];
pStyle.tabStops =
    @[ [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:tabLocation options:[NSDictionary dictionary]] ];

NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] init];
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
textAttachment.image = [UIImage imageNamed:@"test_image"];
textAttachment.bounds = CGRectMake(0, -3, 15, 15);//resize the image
attString = [NSAttributedString attributedStringWithAttachment:textAttachment].mutableCopy;
[attString appendAttributedString:[[NSAttributedString alloc]
                                      initWithString:[NSString stringWithFormat:@"title\t\u2022 %@",
                                                                                [@[ @"description1", @"description2" ]
                                                                                    componentsJoinedByString:@"\n\t\u2022 "]]
                                          attributes:@{NSParagraphStyleAttributeName : pStyle}]];
label.attributedText = attString;

我希望右侧的列表左对齐,但事实并非如此,以下是我得到的结果:

enter image description here

我希望列表能够像这样对齐:

enter image description here

2个回答

4
问题出在NSTextTab中的location参数。
  • According to description, location parameter helps to position text from left margin. So this is what we needed, just replace below lines

    pStyle.tabStops =  @[ [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:tabLocation options:[NSDictionary dictionary]] ];
    

    with

    pStyle.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:[self getTextLocationFor:@"test"]  options:[NSDictionary dictionary]] ];
    
  • Add getTextLocationFor: method to calculate location as follows

    -(CGFloat)getTextLocationFor:(NSString *)inputStr{
         CGSize maximuminputStringWidth = CGSizeMake(FLT_MAX, 30);
         CGRect textRect = [inputStr boundingRectWithSize:maximuminputStringWidth
                                            options:NSStringDrawingUsesLineFragmentOrigin
                                         attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]}
                                            context:nil];
         UIImageView * testImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"close_red"]];//Change image name with yours
    
         return textRect.size.width + testImage.frame.size.width +2;
    }
    
  • That's it we are ready to go run your project now everything will be fine.

结果:

这里输入图片描述


这是一个关于IT技术的图片,无法提供更多信息。

2

如果我理解你的意思正确,那么请尝试以下代码:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 460, 460)];
label.numberOfLines = 0;
[self.view addSubview:label];

NSMutableParagraphStyle *pStyle = [[NSMutableParagraphStyle alloc] init];
pStyle.tabStops = @[ [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:40 options:@{}] ];

NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
textAttachment.image = [UIImage imageNamed:@"img"];
textAttachment.bounds = CGRectMake(0, -3, 30, 30);


NSString *string = [NSString stringWithFormat:@"title\n\r\u2022 %@", [@[ @"description1", @"description2" ] componentsJoinedByString:@"\n\r\u2022 "]];

NSMutableAttributedString *attributedString = [[NSMutableAttributedString attributedStringWithAttachment:textAttachment] mutableCopy];
[attributedString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:string attributes:@{NSParagraphStyleAttributeName : pStyle}]];

label.attributedText = attributedString;

这里是结果

在此输入图片描述

更新

您只能使用TextKit(NSTextLayoutManager)并指定要用于绘制文本的区域,或者使用简单的解决方案并从UIView子类化来实现此目标。

以下是使用视图的解决方案

ListView.h

@interface ListView : UIView

@property(nonatomic,strong) UIImage *image;
@property(nonatomic,strong) NSString *title;
@property(nonatomic,strong) NSArray *list;

@end

ListView.m

static const CGFloat ImageWidth = 13.f;

@interface ListView()
@property (nonatomic,weak) UIImageView *imageView;
@property (nonatomic,weak) UILabel *titleLabel;
@property (nonatomic,weak) UILabel *listLabel;
@end

@implementation ListView
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    [self setup];
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    [self setup];
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    [self setup];
}

- (void)setup {
    UIImageView *imageView = [[UIImageView alloc] init];
    imageView.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:imageView];
    self.imageView = imageView;

    UILabel *titleLabel = [[UILabel alloc] init];
    titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
    titleLabel.numberOfLines = 0;
    [titleLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
    [titleLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
    [self addSubview:titleLabel];
    self.titleLabel = titleLabel;

    UILabel *listLabel = [[UILabel alloc] init];
    listLabel.translatesAutoresizingMaskIntoConstraints = NO;
    listLabel.numberOfLines = 0;
    [listLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
    [listLabel setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
    [self addSubview:listLabel];
    self.listLabel = listLabel;

    NSDictionary *views = NSDictionaryOfVariableBindings(imageView,titleLabel,listLabel);
    NSDictionary *metrics = @{ @"ImageHeight" : @(ImageWidth) };

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[imageView(ImageHeight)]-0-[titleLabel]-0-[listLabel]-0-|" options:0 metrics:metrics views:views]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[imageView(ImageHeight)]" options:NSLayoutFormatAlignAllTop metrics:metrics views:views]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[titleLabel]" options:NSLayoutFormatAlignAllTop metrics:metrics views:views]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[listLabel]-0-|" options:NSLayoutFormatAlignAllTop metrics:metrics views:views]];
}

- (void)setImage:(UIImage *)image {
    _image = image;
    self.imageView.image = image;
    [self setNeedsLayout];
}

- (void)setTitle:(NSString *)title {
    _title = title;
    self.titleLabel.text = title;
    [self setNeedsLayout];
}

- (void)setList:(NSArray *)list {
    _list = list;

    NSMutableParagraphStyle *pStyle = [[NSMutableParagraphStyle alloc] init];
    pStyle.tabStops = @[ [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:40 options:@{}] ];

    NSString *string = [NSString stringWithFormat:@"\u2022 %@", [list componentsJoinedByString:@"\n\u2022 "]];
    self.listLabel.attributedText = [[NSAttributedString alloc] initWithString:string attributes:@{NSParagraphStyleAttributeName : pStyle}];

    [self setNeedsLayout];
}

@end

谢谢您的回答,您的解决方案不起作用,我不明白为什么要使用\r? - Mosbah
你能展示一下你期望的结果,我再修正我的答案吗? - Konstantin
我更新了我的问题,并附上了期望的输出,谢谢! - Mosbah
你的解决方案是否可以使用两个标签? - Konstantin
我考虑过这个,但是可能会比较棘手,因为我有多个类似的列表要根据数据显示。 - Mosbah
我无法使用一个属性字符串实现这些功能。你可以从视图中继承子类,并在应用程序中使用它,或者使用苹果公司的TextKit(尤其是LayoutManager)。 - Konstantin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接