0

这是我在设置一些UIViews 和子视图时遇到的几个问题中的第一个。我有一个UIView在运行时动态定位在屏幕上的。那UIView(master) 包含另一个UIView(child) 包装 aUIImageView和 a UILabel。以下是我对这种安排的要求:

  1. 设备旋转时,子设备UIView必须保持在主设备的中心。UIView
  2. 中的文字UILabel可以很长也可以很短,UIView带有图像和文字的孩子仍然必须保持居中。
  3. 我想避免子类化 UIView 来处理这种情况,我还想避免任何框架/定位代码,willRotateToInterfaceOrientation.我想用autoresizingMaskIB 中的一些设置来处理所有这些,如果可能的话,可能还有一些强制调整大小的代码。

这是 Interface Builder 中的控件排列(以红色突出显示):

Interface Builder 控件配置

使用 Interface Builder,autoresizingMask属性已像这样设置,用于描述的控件

  • UIView(master): 弹性上边距、弹性左边距、弹性右边距、弹性宽度
  • UIView(子):灵活的上边距,灵活的下边距,灵活的左边距,灵活的右边距,灵活的宽度,灵活的高度。(所有模式,无除外)
  • UIImageView: 灵活的右边距
  • UILabel: 灵活的右边距

这是在纵向模式下在运行时以编程方式添加后的视图(带有图像和文本的红色条):

主人UIView的背景是浅红色的图像。childUIView的背景比这稍微暗一些,而UILabel's 的背景更暗。我为它们着色,以便在应用程序响应旋转时可以看到它们的边界。

在此处输入图像描述

我很清楚:

  • 它不是居中的,但...
  • 在将文本从“此地图范围内没有数据”更改为 IB 中的默认值后。到“TEST1, 123”。标签正确收缩。

这是在纵向添加然后旋转到横向模式后的视图:

在此处输入图像描述

从这里我可以看到:

  • 它仍然没有居中,并且可能在旋转之前位于其原始框架原点
  • UIView(子)已扩展以填充更多不应该的屏幕。
  • UIView(主)已正确扩展以填充屏幕宽度。

这就是让我走到现在的代码。showNoDataStatusView我从 viewDidLoad调用该方法:

// Assuming

#define kStatusViewHeight 20

- (void)showNoDataStatusView {

    if (!self.noDataStatusView.superview) {

        self.noDataStatusView.frame = CGRectMake(self.mapView.frame.origin.x,
                                             self.mapView.frame.origin.y,
                                             self.mapView.frame.size.width,
                                             kStatusViewHeight);

        self.noDataStatusView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bgRedStatus.png"]];

        // Position the label view in the center
        self.noDataStatusLabelView.center = CGPointMake(self.noDataStatusView.frame.size.width/2,
                                                    self.noDataStatusView.frame.size.height/2);

        // Test different text
        self.noDataStatusLabel.text = @"Testing, 123.";

        // Size to fit label
        [self.noDataStatusLabel sizeToFit];

        // Test the status label view resizing
        [self.noDataStatusLabelView resizeToFitSubviews];

        // Add view as subview
        [self.view addSubview:self.noDataStatusView];

    }
}

请注意以下事项:

  • resizeToFitSubviews是我放置在 UIView 上的一个类别,一旦我发现 UIView 不会自动调整大小以适应它们的子视图,即使你调用sizeToFit. 这个问题这个问题解释了这个问题。请参阅下面的类别代码。
  • 我曾考虑过创建一个 UIView 子类来为我处理所有这些逻辑,但这似乎有点矫枉过正。在IB中安排这个应该很简单吧?
  • 我已经尝试设置书中的每个调整大小掩码设置,以及调整标签和视图大小调整发生的顺序以及将主视图添加为子视图的点。似乎没有任何效果,因为我每次都会得到奇怪的结果。

UIView resizeToFitSubviews 类实现方法:

-(void)resizeToFitSubviews
{
    float width = 0;
    float height = 0;

    // Loop through subviews to determine max height/width
    for (UIView *v in [self subviews]) {

        float fw = v.frame.origin.x + v.frame.size.width;
        float fh = v.frame.origin.y + v.frame.size.height;

        width = MAX(fw, width);
        height = MAX(fh, height);
    }

    [self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, width, height)];
}

我想知道的是为什么UIView(子)在将其超级视图添加到视图层次结构后没有正确居中。它看起来好像得到了正确的宽度,但是当标签显示“此地图范围内没有数据”时,它以某种方式保留了它在 IB 中的帧。

我还想知道为什么它在设备旋转后没有居中,以及我在这里采用的方法是否明智。也许这导致了我遇到的其他问题。任何 UIView 布局帮助将不胜感激。

谢谢!

4

2 回答 2

1

如果您能够以 iOS 6 为目标,您可以使用新的 Auto Layout 功能使这更容易管理 - 我一直在阅读 Ray Wenderlich 的精彩教程,它似乎完美地解决了您所看到的问题。

于 2013-01-08T21:55:46.637 回答
1

这里的问题是我的UIView(主)在设备旋转时不会自动布局它的子视图,并且用于定位图像和内部的“弹簧和支柱”布局方法UIView效率低下。我通过做两件事解决了这个问题。

  1. 我摆脱了内部UIView(子)实例,只留下了UIView(主)和内部的 a UILabeland UIImageView
  2. 然后我创建了一个UIView名为的子类StatusView,并在其中实现了该layoutSubviews方法。在其构造函数中,我添加了一个UIImageViewandUILabel并动态定位它们。UILabel首先根据文本的大小定位,然后将 放置在文本的UIImageView左侧并垂直居中。就是这样。在layoutSubviews我确保为新框架调整元素的位置。

此外,由于在某些情况下我需要交换背景、消息和可能的图像,因此使用自定义类是有意义的。这里/那里可能存在内存问题,但是当我使用分析工具运行此问题时,我会解决它们。

最后,我不完全确定这段代码是否坚如磐石,但它确实有效。我也不知道我的 init 方法中是否需要布局代码。布局子视图似乎在视图被添加为子视图后不久被调用。

这是我的班级标题:

#import <UIKit/UIKit.h>

typedef enum {
    StatusViewRecordCountType = 0,
    StatusViewReachedMaxRecordCountType = 1,
    StatusViewZoomInType = 2,
    StatusViewConnectionLostType = 3,
    StatusViewConnectionFoundType = 4,
    StatusViewNoDataFoundType = 5,
    StatusViewGeographyIntersectionsType = 6,
    StatusViewRetreivingRecordsType = 7
} StatusViewType;

@interface StatusView : UIView

@property (strong, nonatomic) NSString *statusMessage;
@property (nonatomic) StatusViewType statusViewType;

- (id)initWithFrame:(CGRect)frame message:(NSString*)message type:(StatusViewType)type;

@end

...和实施:

#import "StatusView.h"

#define kConstrainSizeWidthOffset 10
#define kImageBufferWidth 15

@interface StatusView ()

@property (nonatomic, strong) UILabel *statusMessageLabel;
@property (nonatomic, strong) UIFont *statusMessageFont;
@property (nonatomic, strong) UIImage *statusImage;
@property (nonatomic, strong) UIImageView *statusImageView;

@end

@implementation StatusView

@synthesize statusMessageLabel = _statusMessageLabel;
@synthesize statusMessageFont = _statusMessageFont;
@synthesize statusImageView = _statusImageView;
@synthesize statusMessage = _statusMessage;
@synthesize statusViewType = _statusViewType;
@synthesize statusImage = _statusImage;

- (id)initWithFrame:(CGRect)frame message:(NSString *)message type:(StatusViewType)type {

    self = [super initWithFrame:frame];

    if (self) {

        if (message != nil) {

            _statusMessage = message;
            _statusMessageFont = [UIFont fontWithName:@"Avenir-Roman" size:15.0];

            CGSize constrainSize = CGSizeMake(self.frame.size.width - kImageBufferWidth - kConstrainSizeWidthOffset, self.frame.size.height);

            // Find the size appropriate for this message
            CGSize messageSize = [_statusMessage sizeWithFont:_statusMessageFont constrainedToSize:constrainSize];

            // Create label and position at center of status view
            CGRect labelFrame = CGRectMake(self.frame.origin.x,
                                       self.frame.origin.y,
                                       messageSize.width,
                                       messageSize.height);

            _statusMessageLabel = [[UILabel alloc] initWithFrame:labelFrame];
            _statusMessageLabel.backgroundColor = [UIColor clearColor];
            _statusMessageLabel.textColor = [UIColor whiteColor];
            _statusMessageLabel.font = _statusMessageFont;

            // Set shadow and color
            _statusMessageLabel.shadowOffset = CGSizeMake(0, 1);
            _statusMessageLabel.shadowColor = [UIColor blackColor];

            // Center the label
            CGPoint centerPoint = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
            _statusMessageLabel.center = centerPoint;

            // Gets rid of fuzziness
            _statusMessageLabel.frame = CGRectIntegral(_statusMessageLabel.frame);

            // Flex both the width and height as well as left and right margins
            _statusMessageLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;

            // Set label text
            _statusMessageLabel.text = _statusMessage;

            [self addSubview:_statusMessageLabel];
        }

        self.statusViewType = type;

        if (_statusImage != nil) {

            // Create image view
            _statusImageView = [[UIImageView alloc] initWithImage:_statusImage];

            // Vertically center the image
            CGPoint centerPoint = CGPointMake(_statusMessageLabel.frame.origin.x - kImageBufferWidth,
                                          self.frame.size.height / 2);

            _statusImageView.center = centerPoint;

            [self addSubview:_statusImageView];
        }
    }

    return self;
}

- (void)layoutSubviews {

    CGSize constrainSize = CGSizeMake(self.frame.size.width - kImageBufferWidth - kConstrainSizeWidthOffset, self.frame.size.height);

    // Find the size appropriate for this message
    CGSize messageSize = [_statusMessage sizeWithFont:_statusMessageFont constrainedToSize:constrainSize];

    // Create label and position at center of status view
    CGRect labelFrame = CGRectMake(self.frame.origin.x,
                               self.frame.origin.y,
                               messageSize.width,
                               messageSize.height);

    _statusMessageLabel.frame = labelFrame;

    // Center the label
    CGPoint centerPoint = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
    _statusMessageLabel.center = centerPoint;

    // Gets rid of fuzziness
    _statusMessageLabel.frame = CGRectIntegral(_statusMessageLabel.frame);

    if (_statusImageView != nil) {

        // Vertically center the image
        CGPoint centerPoint = CGPointMake(_statusMessageLabel.frame.origin.x - kImageBufferWidth,
                                      self.frame.size.height / 2);

        _statusImageView.center = centerPoint;
    }
}

#pragma mark - Custom setters

- (void)setStatusMessage:(NSString *)message {

    if (_statusMessage == message) return;

    _statusMessage = message;

    _statusMessageLabel.text = _statusMessage;

    // Force layout of subviews
    [self setNeedsLayout];
    [self layoutIfNeeded];
}

- (void)setStatusViewType:(StatusViewType)statusViewType {

    _statusViewType = statusViewType;

    UIColor *bgColor = nil;

    switch (_statusViewType) {

        // Changes background and image based on type
    }

    self.backgroundColor = bgColor;

    if (_statusImageView != nil) {
        _statusImageView.image = _statusImage;
    }
}

@end

然后在我的视图控制器中我可以这样做:

CGRect statusFrame = CGRectMake(self.mapView.frame.origin.x,
                                    self.mapView.frame.origin.y,
                                    self.mapView.frame.size.width,
                                    kStatusViewHeight);

self.staticStatusView = [[StatusView alloc] initWithFrame:statusFrame message:@"600 records found :)" type:StatusViewRecordCountType];

self.staticStatusView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;

[self.view addSubview:self.staticStatusView];

...稍后我可以通过这样做来改变它:

self.staticStatusView.statusMessage = @"No data was found here";
self.staticStatusView.statusViewType = StatusViewNoDataFoundType;

现在我有了一个可重用的类,而不是 12 个 UIView 实例,它们在我的 NIB 周围浮动,具有各种设置和属性。

于 2013-01-10T15:42:21.643 回答