0

UIViewこれは、私がいくつかのサブビューを設定しているいくつかの問題の最初のものです。UIView実行時に画面上に動的に配置されるがあります。そのUIView(マスター)には、aとaUIViewをラップする別の(子)が含まれています。この取り決めのために私が持っている要件は次のとおりです。UIImageViewUILabel

  1. デバイスが回転するとき、子供UIViewはマスターの中心に留まらなければなりません。UIView
  2. のテキストはUILabel非常に長くても短くてUIViewもかまいませんが、画像とテキストを含む子は中央に配置したままにする必要があります。
  3. このシナリオを処理するためにUIViewをサブクラス化することは避けたいと思います。また、IBのいくつかの設定と、可能であれば少し強制的なサイズ変更コードを使用willRotateToInterfaceOrientation.して、これらすべてを処理したいフレーム/ポジショニングコードも避けたいと思います。autoresizingMask

これは、Interface Builderのコントロールの配置です(赤で強調表示されています)。

InterfaceBuilderの制御装置

Interface Builderでは、autoresizingMask説明されているコントロールのプロパティがそのように設定されています

  • UIView(マスター):柔軟な上マージン、柔軟な左マージン、柔軟な右マージン、柔軟な幅
  • UIView(子):柔軟な上マージン、柔軟な下マージン、柔軟な左マージン、柔軟な右マージン、柔軟な幅、柔軟な高さ。(なしを除くすべてのモード)
  • UIImageView:柔軟な右マージン
  • UILabel:柔軟な右マージン

これは、ポートレートモードで実行時にプログラムで追加された後のビュー(画像とテキストを含む赤いバー)です。

マスターUIViewの背景は明るい赤色の画像です。子供UIViewの背景はそれよりもわずかに暗く、UILabelの背景はさらに暗くなります。アプリが回転に反応したときに境界が見えるように、色を付けました。

ここに画像の説明を入力してください

私には次のことが明らかです。

  • 中央に配置されていませんが...
  • テキストをIBのデフォルト値から「このマップエクステントにデータがありません」から変更した後。「TEST1、123」へ。ラベルは正しく収縮します。

これは、縦向きで追加され、横向きモードに回転された後のビューです。

ここに画像の説明を入力してください

ここから私はそれを見ることができます:

  • それはまだ中央に配置されておらず、おそらく回転前の元のフレームの原点にあります
  • UIView(子)は、表示されるべきではないときに画面全体に表示されるように拡張されました。
  • UIView(マスター)が画面幅に合わせて適切に拡張されました。

これは私が今いる場所に私を導いたコードです。showNoDataStatusViewviewDidLoadからメソッドを呼び出します。

// 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

ここでの問題はUIView、デバイスが回転したときに私の(マスター)がサブビューを自動的にレイアウトせず、画像と内部を配置するために使用される「スプリングとストラット」のレイアウト方法UIViewが非効率的だったことです。私は2つのことをして問題を解決しました。

  1. 内部(子)インスタンスを削除し、 (マスター)とその内部のみUIViewを残しました。UIViewUILabelUIImageView
  2. UIView次に、というサブクラスを作成し、StatusViewその中にメソッドを実装しlayoutSubviewsます。そのコンストラクターで、UIImageViewとを追加し、UILabelそれらを動的に配置します。は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 に答える
1

iOS 6 をターゲットにすることができる場合は、新しい自動レイアウト機能を使用して、これをはるかに簡単に管理できます。Ray Wenderlich による優れたチュートリアルを読んでいます。これは、あなたが見ている問題を解決するのに最適だと思われます。

于 2013-01-08T21:55:46.637 に答える