在运行时和方向更改期间居中和sizeToFit子视图

时间:2013-01-08 20:42:36

标签: ios uiview subviews sizetofit

这是我设置一些UIView和子视图的几个问题中的第一个。我有一个UIView在运行时动态定位在屏幕上。 UIView(主)包含另一个UIView(子),其中包含UIImageViewUILabel。以下是我对此安排的要求:

  1. 当设备旋转时,孩子UIView必须在主人UIView中居中。
  2. UILabel中的文字可能很长或很短,带图片和文字的儿童UIView仍必须保持居中。
  3. 我想避免继承子类UIView来处理这种情况,我还想避免willRotateToInterfaceOrientation.中的任何框架/定位代码我想通过一些autoresizingMask设置来处理所有这些IB如果可能的话,也许有点强制调整代码大小。
  4. 这是Interface Builder中的控件排列(以红色突出显示):

    Interface Builder control arrangement

    使用Interface Builder,autoresizingMask属性已设置如此,用于描述的控件

    • UIView(主):灵活的上边距,灵活的左边距,灵活的右边距,灵活的宽度
    • UIView(儿童):灵活的上边距,灵活的下边距,灵活的左边距,灵活的右边距,灵活的宽度,灵活的高度。 (所有模式,除无)
    • UIImageView:灵活的右边距
    • UILabel:灵活的右边距

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

    主人UIView的背景是浅红色的图像。儿童UIView的背景略暗于此,UILabel的背景更暗。我对它们进行着色,以便在应用程序响应轮换时我可以看到它们的边界。

    enter image description here

    我很清楚:

    • 它没有居中但是......
    • 从“此地图范围中没有数据”中更改I.B中默认值的文本。到“TEST1,123”。标签合同正确。

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

    enter image description here

    从这里我可以看到:

    • 它仍未居中,可能在旋转前的原始帧原点
    • UIView(孩子)已经扩展到不应该填充更多屏幕。
    • UIView(主)已正确扩展以填充屏幕宽度。

    这是我现在所处位置的代码。我从viewDidLoad调用方法showNoDataStatusView

    // 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上的一个类别,一旦我发现即使你打电话给sizeToFit,UIView也不会自动调整大小以适应他们的子视图。 This questionthis question解释了这个问题。请参阅下面的类别代码。
    • 我考虑过为我创建一个处理所有这些逻辑的UIView子类,但它似乎有些过分。将其安排在I.B.中应该很简单。正确?
    • 我尝试设置书中的每个大小调整蒙版设置,以及调整标签和视图大小调整的顺序以及主视图作为子视图添加的点。似乎没有什么工作,因为我每次都会得到奇怪的结果。

    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(子)在将超级视图添加到视图层次结构后没有正确居中。它看起来好像有适当的宽度,但不知何故保留了它在I.B.中的框架。当标签显示“此地图范围内没有数据。”

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

    谢谢!

2 个答案:

答案 0 :(得分:1)

如果您能够定位iOS 6,您可以使用新的自动布局功能,使这更容易管理 - 我一直在阅读Ray Wenderlich的great tutorial,这似乎是完美的解决方案你看到的问题。

答案 1 :(得分:1)

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

  1. 我删除了内部UIView(子)实例,只留下UIView(主),并且只留下UILabelUIImageView
  2. 然后我创建了一个名为UIView的{​​{1}}子类,并在其中实现了StatusView方法。在其构造函数中,我添加了layoutSubviewsUIImageView并动态定位它们。 UILabel首先根据文字的大小定位,然后将UILabel放在其左侧并垂直居中。而已。在UIImageView中,我确保为新框架调整元素的位置。
  3. 此外,由于我需要在某些情况下交换背景,消息和可能的图像,因此使用自定义类是有意义的。这里/那里可能存在内存问题,但是当我使用分析工具运行时,我会解决它们。

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

    这是我的班级标题:

    layoutSubviews

    ......和实施:

    #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];
    

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