ios 自動換行flowLayout

android 自動換行flowLayout


最近產品需要實現自動換行功能,在gitHub看了一下,雖然有不少,但都有那麼一點不滿足需求的,或者感覺用着不方便的。所以乾脆自己寫了一份,順便在有時間時寫了一份ios的版本,有興趣想看android版的的可點擊上面鏈接。


在這裏先提供下載地址:https://github.com/lanqi-x/ios_FlowLayout

然後來個圖先:



實現概要思路爲:繼承UIView,複寫-(instancetype)initWithFrame:(CGRect)frame(代碼創建)、-(instancetype)initWithCoder:(NSCoder *)aDecoder(xib或storyboard)、-(void)addSubview:(UIView *)view和- (void)layoutSubviews。

好,要開始吧啦吧啦了。ios版相對android版來說會比較簡單,但相對的坑也比較多,一些get、set方法在此就不說了,代碼註釋還蠻多的,直接講addView和layoutSubviews來個方法就好了。

1、首先addview

-(void)addSubview:(UIView *)view{
    [self.subViewList addObject:view];
    [super addSubview:view];
}
從代碼就可以理解了,這個很簡單,複寫只爲將view保存到我的數組之中,方便維護。

2、layoutSubviews

layoutSubviews比較複雜,首先oc也沒有像java那樣方便的內部類(普通類也可以,只是這裏使用內部類在一些方法和屬性上的調用會方便一些),所以我將每行的view和高度分別放在了兩個數組之中。然後ios的View沒有像android的view一樣有測量方法,於是我把測量和子view的擺放全丟在layoutSubviews這個方法上了,所以我將分爲計算和擺放兩步來解釋下我的實現思路。

(1)計算每行View的個數和每行的高度

這個也不難,就是對在addview存起來的view進行一次for循環,用sumWidth加入view以後所佔的高度,即sumWidth+=view的寬度+view之間的間隔。情況一、sumWidth不大於flowLayout的寬,則將view添加到lineList中,比較該view的高度是否大於當前行高度height,如果大於則該行的高度等於view的高度。

情況二、sumWidth大於flowLayout的寬,那麼將lineList加到rowList中記爲一行,將height加到rowHeightList中記爲該行的高度,height等於當前view的高度作爲新一行的高度,重新new lineList,將view加到其中作爲新的一行。

情況三、view爲最後一個,那麼不管夠不夠一行都記爲一行,將lineList加到rowList中

關鍵代碼如下:

if (self.subViewList.count>0) {
        //清空所有值,重新計算
        [self.rowHightList removeAllObjects];
        [self.rowList removeAllObjects];
        
        //獲取第一個view
        NSMutableArray *lineList=[NSMutableArray array];
        UIView* firstView=self.subViewList[0];
        CGFloat sumWidth=self.horizontalSpace+firstView.frame.size.width;
        CGFloat height=firstView.frame.size.height;
        CGFloat flowWidth=self.frame.size.width;
 
        [lineList addObject:firstView];
        
        //計算每行放哪些View
        for (int i=1; i<self.subViewList.count; i++) {
            UIView* view=self.subViewList[i];
            sumWidth=sumWidth+view.frame.size.width+self.horizontalSpace;       
            if (sumWidth >= flowWidth) {
                
                //一行放不下了,記錄爲一行
                [self saveLine:lineList lineHeight:height];
                
                //重新創建一行
                lineList=[NSMutableArray array];
                [lineList addObject:view];
                height=view.frame.size.height;
                sumWidth=self.horizontalSpace+view.frame.size.width;
                
            }else{
                //一行還夠放
                [lineList addObject:view];
                if (view.frame.size.height>height) {
                    height=view.frame.size.height;
                }
            }
            //如果這是最後一個view了,不管夠不夠一行,都記爲一行
            if (i==self.subViewList.count-1) {
                [self saveLine:lineList lineHeight:height];
            }
        }


(2)擺放view

ios的辦法不需要像android那樣調用view的layout方法,而是改變view的x,y值。這裏使用rowList進行雙重for循環,這裏關鍵的有五點

(1)每行的左上角座標及起始y值,這裏爲行間距加上一行的高度(知道爲什麼上面計算那裏要存每行的高度了吧)。

(2)每個view的x座標,這裏爲view之間的間距加上上個view的寬度。

(3)view在該行居中顯示,如果view的高度小於行的高度(這裏再次用的行高度了),那麼該view的y爲y+=(lineHeight-view.width)/2(這裏只是僞代碼哈)

(4)判讀是否超過FlowLayout的限定高度或最大行數,如果是,那麼剩下的view都不顯示(由於ios不像android,android有viewGroup,viewGroup不擺放子view時,子view是直接不顯示的,而ios則用原始的frame值進行擺放,所以我這裏爲了不讓他顯示在界面上,直接將這些位置超過限制的子view進行了hidden)

(5)記錄顯示的最後一個view在subViewList的下標是多少。(用於當FlowLayout的高度設置爲根據內容時重新計算FlowLayout的最終高度)

 

       //擺放subView
        CGFloat y=self.verticalSpace;
        //一行一行取出
        for (int i=0;i<self.rowList.count;i++) {
            lineList=self.rowList[i];
            CGFloat x=self.horizontalSpace;
            //獲取這一行的高度
            CGFloat rowHeight=[self.rowHightList[i] floatValue];
            
            if (i!=0) {
                y=y+[self.rowHightList[i-1] floatValue]+self.verticalSpace;
            }
            
            if ((self.fastenHeight && y+[self.rowHightList[i] floatValue]>=self.frame.size.height)
                || i>=self.maxLine) {
                [self dontShowLine:i];
                break;
            }
            
            //擺放每一行的subView
            for (UIView *item in lineList){
                _lastShowIndex++;
                [self setX:x changeView:item];
                item.hidden=false;
                if (item.frame.size.height<rowHeight) {
                    [self setY:y+((rowHeight-item.frame.size.height)/2.0) changeView:item];
                }else{
                    [self setY:y changeView:item];
                }
                x=x+item.frame.size.width+self.horizontalSpace;
            }
            
        }


如果設置FlowLayout高度是根據內容決定的,那麼重新調整一下FlowLayout的最終高度,首先獲取顯示的最後一個view的底部Y值(CGRectGetMaxY(lastItem.frame))加上行距作爲FlowLayout最終的高度,這裏做個兩個處理一是將frame的height改爲最終的高,二是如果FlowLayout存在NSLayoutAttributeHeight約束,那麼就將其移除,添加新的NSLayoutAttributeHeight約束值爲FlowLayout最終的高度。

       if(!self.fastenHeight && self.lastShowIndex!=-1){
            UIView *lastItem = self.subViewList[self.lastShowIndex];
            CGRect rect=self.frame;
            //計算flowLayout最終的高度
            rect.size.height=CGRectGetMaxY(lastItem.frame) + self.verticalSpace;
            self.frame = rect;
////            修改約束,保證兄弟或父子控件的約束更新
            NSArray* constrains = self.constraints;
            for (NSLayoutConstraint* constraint in constrains) {
                if (constraint.firstAttribute == NSLayoutAttributeHeight) {
                    [self removeConstraint:constraint];
                    [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:rect.size.height]];
                }
            }  
        }


FlowLayout我的實現思路就是這樣了,相比android版,這裏還剩每行最後剩餘空間的分配模式還沒實現,以後有時間再補上。最後再提個小坑,就是最終修改FlowLayout的高度那裏,如果你的高不是通過NSLayoutAttributeHeight確定,而是通過NSLayoutAttributeTop加NSLayoutAttributeBottom確定的,那麼這裏就不會更新了,那麼如果FlowLayout下方有其他view,界面上就有可能會出現兩個view覆蓋的情況,因爲個人覺得如果需求是想其高度根據內容決定的,那麼一般應該是設置NSLayoutAttributeHeight約束的,所以這裏不做處理。如果想處理,可以在FlowLayout中定義一個協議,在最後將計算得到的最終高度傳給這個協議,提供給外部實時獲取到FlowLayout的高度變化。

好,本文結束,如有說的不好的地方請多多包涵,也可在評論中指點一下。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章