IOS 自動佈局-UIStackPanel和UIGridPanel(四)

爲何說scrollview的自動化佈局是難點?佈局

對scrollview作自動化佈局,無非就是想對scrollview裏面的subviews來作自動化佈局。可是scrollview裏面的subviews的自動化佈局不是由scrollview的高寬來決定的,而是由scrollview的contentSize共同決定的,這樣就出現一個問題了,就算scrollview的高寬是改變了,可是隻要contentSize不變,那麼對於scrollview裏面的subviews的高寬實際上是沒有影響的。而實現自動化佈局的NSLayoutConstraint也沒法實現對scrollview的contentSize屬性作自動化佈局的。那麼純粹的想使用NSLayoutConstraint來對scrollview作自動化佈局的方法是行不通的。ui

到這裏咱再換一個方法,其實咱們日常用scrollview更多的場景是用來作上下滾動或左右滾動,不多有上下滾動和左右滾動同時存在的狀況。如今假設咱們的自動化佈局的scrollview就是上下滾動的,在水平方向,subviews的寬度永遠跟scrollview的寬度一致,這樣的場景是否是能實現?針對這樣的場景咱們立刻就能想到,只要把subviews的寬度用NSLayoutConstraint實現跟scrollview的寬度綁定就能夠了啊。spa

    UIScrollView *scroll=[[UIScrollView alloc] init];
    scroll.backgroundColor=[UIColor blueColor];
    scroll.isBindSizeToSuperView=YES;
    [self.view addSubview:scroll];
    [scroll setContentSize:CGSizeMake(100, 1000)];
    
    UILabel *label = [[UILabel alloc] initWithSize:CGSizeMake(100, 50)];
    label.backgroundColor = [UIColor blackColor];
    label.textColor=[UIColor whiteColor];
    label.font=[UIFont systemFontOfSize:12];
    label.text = @"Label1";
    label.translatesAutoresizingMaskIntoConstraints=NO;
    label.textAlignment = NSTextAlignmentCenter;
    [scroll addSubview:label];
    [scroll addConstraint:[NSLayoutConstraint constraintWithItem:label
                                                       attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scroll attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0]];

咱們發現這樣作能夠啊!別急,你試着再添加一個subview看看?你會發現用這樣的方法雖然能讓subviews實現寬度跟scrollview的寬度保持一致,可是在垂直方向上沒法順序佈局。這是爲何呢?code

問題在於translatesAutoresizingMaskIntoConstraints這個屬性咱們設置了no,爲何要設置no?只要咱們使用自動佈局,或者更通俗的說,只要咱們使用了NSLayoutConstraint,那麼必需要把這個屬性設置爲no。而一旦設置了no,那麼這個view的位置和大小也只能是經過NSLayoutConstraint來實現了。說道這裏,看過我前三篇博客的同窗就能想到,不是有一個UIStackPanel正好能夠實現這樣的功能的嗎?對的。咱們能夠直接拿來用,把uiStackpanel作未subview添加到scrollView中,而後將原本要添加到scrollvew中的subviews添加到stackpanel中。這樣就能實現以上的場景了。可是這樣作仍是有一個問題,就是stackpanel的寬度咱們是能夠綁定到scrollview的寬度,可是高度呢?高度必須跟contentSize的height作綁定。而要實現這樣的需求咱們就得藉助IOS的KVO技術來實現,獲取scrollview的contentSize屬性變化事件,而後再次綁定。這樣就能徹底的實現以上的需求了。server

爲了把以上的實現過程進行一個封裝,咱們在UIPanel裏面添加了一個bindToScrollView的方法。blog

NSString *const KVCUIPanelString_ContentSize = @"scrollView_ContentSize";
-(void)bindToScrollView:(UIScrollView *)scrollView{
    _scrollView=scrollView;
    self.translatesAutoresizingMaskIntoConstraints=NO;
    
    [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                           attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0]];
    
    [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                           attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0]];
    
    [scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                           attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeTop multiplier:1.0f constant:0]];
    
    [scrollView addObserver:self forKeyPath:KVCUIPanelString_ContentSize options:NSKeyValueObservingOptionNew context:nil];
    [self addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                     attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:[scrollView contentSize].height]];//第一次綁定的時候直接把scrollView的contentSize.height做爲panel的高度
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    if([keyPath isEqualToString:KVCUIPanelString_ContentSize]){
        [self removeConstraints:self.constraints];
        [self addConstraint:[NSLayoutConstraint constraintWithItem:self
                                                         attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:[(UIScrollView *)object contentSize].height]];
    }
}

使用kvo的時候必定要記得釋放。事件

-(void)removeFromSuperview{
    [super removeFromSuperview];
    if(_scrollView){
        [_scrollView removeObserver:self forKeyPath:KVCUIPanelString_ContentSize context:nil];
        _scrollView=nil;
    }
}

方法已經封裝好了,那麼後面就是如何使用了。代碼以下:ip

    //初始化UIScrollView
    UIScrollView *scroll=[[UIScrollView alloc] init];
    scroll.backgroundColor=[UIColor blueColor];
    scroll.isBindSizeToSuperView=YES;//把UIScrollView的高寬綁定到父視圖
    [self.view addSubview:scroll];
    [scroll setContentSize:CGSizeMake(100, 1000)];//設置UIScrollView的contentSize
    
    UIStackPanel *_panel=[[UIStackPanel alloc] init];
    [scroll addSubview:_panel];
    _panel.backgroundColor=[UIColor yellowColor];
    [_panel bindToScrollView:scroll];//將UIStackPanel綁定到UIScrollView

    UILabel *label = [[UILabel alloc] initWithSize:CGSizeMake(100, 50)];
    label.backgroundColor = [UIColor blackColor];
    label.textColor=[UIColor whiteColor];
    label.font=[UIFont systemFontOfSize:12];
    label.text = @"Label1";
    label.textAlignment = NSTextAlignmentCenter;
    [_panel addSubview:label];

    label = [[UILabel alloc] initWithSize:CGSizeMake(100, 50)];
    label.backgroundColor = [UIColor blackColor];
    label.textColor=[UIColor whiteColor];
    label.font=[UIFont systemFontOfSize:12];
    label.text = @"Label2";
    label.margin=UIEdgeInsetsMake(10, 0, 0, 0);
    label.textAlignment = NSTextAlignmentCenter;
    [_panel addSubview:label];

至此,uiscrollview的自動化解決方案已經完成了。rem

 

下一遍介紹UIView在如何停靠在superView中,實現無論superview的高寬如何改變,都不會改變UIView的停靠位置。源碼

連帶本篇的源碼都會在下一篇中給出。

相關文章
相關標籤/搜索