【IOS】自訂可點擊的多文本跑馬燈YFRollingLabel,iosyfrollinglabel

來源:互聯網
上載者:User

【IOS】自訂可點擊的多文本跑馬燈YFRollingLabel,iosyfrollinglabel

需求

項目中需要用到跑馬燈來僅展示一條訊息,長度合適則不滾動,過長則迴圈滾動。

雖然不是我寫的,但看了看代碼,是在一個UIView裡面放入兩個UILabel,

在前一個快結束的時候,另一個顯示。然而點擊處理的 確是UIView的點擊事件。    

 

然而看到比如地鐵、公交裡面的跑馬燈是分了很多段顯示的。雖然說可以將多段合并為一段來顯示,

但是如果各個需要點擊事件又該如何處理呢?於是我來自己實現可點擊的多段跑馬燈。

所以這篇隨筆我要實現的跑馬燈包含下面這種效果:(圖中有5段   點擊不同文本可觸發相應的事件)

 

彎路

還記得上一篇隨筆【IOS】將字型大小不同的文字底部對齊 嗎?

雖然不能夠做到多個UILabel的底部對齊,但是我們可以通過繼承UILabel來改變文本豎直方向的位置。

所以呢,我最初的想法是繼承UILabel,可以保持其繼承性, 通過NSTimer來直接慢慢移動UILable裡面的文本。

這裡出現了兩個問題:(以@"這是自訂跑馬燈裡面要移動的文本"為例)

1.移動是可以移動,但是在文本左移至快要看不見(只剩下"移動的文本")的時候, 如何讓@"這是.."開始從右側出現呢?

2.文本過長的時候,看不見的部分將被截斷,所以在移動的時候,只有部分文本了。

第一種好像沒有辦法,UILabel只存在一個文本的bounds, 不可能讓他一部分在左邊, 一部分在右邊。 

第二種就因為存在預設的屬性NSLineBreakMode:NSLineBreakByWordWrapping,就算不截斷文本也只會變為省略符號。

所以這種方法作罷。。。。

 

實現

首先要明確的是本跑馬燈繼承了UIView且需要兩個UILabel、定時器NSTimer。

在初始化時,傳入字串數組,並計算各個字串的自適應大小

CGRect textRect = [((NSString *)_textArray[i]) boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:kFont} context:nil];[_textRectArray addObject:[NSValue valueWithCGRect:textRect]];

如果傳入的字串數組個數為1且自適應寬度<UIView寬度,則不會滾動。重新寫一個UILabel用於顯示就行了

其他情況下,就是可以滾動的, 在此執行個體化兩個UILabel,並開啟定時器了:

定時器相關:

_timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
//為什麼要將定時器起放入LOOP中呢?
//如果此RunLoop正在執行一個連續性的運算,timer就會被延時出發。
//也就是說,如果你將跑馬燈放入scrollview上,當滑動scrollview的時候,定時器就不會動了

//相關方法:
//[_timer setFireDate:[NSDate date]];   開始//[_timer setFireDate:[NSDate distantFuture]]; 暫停
//取消定時器
//[_timer invalidate]; //_timer = nil;  //防止野指標

//定時器執行事件:

-(void)timerAction:(NSTimer *)timer{}

現在我們要做的  就是在每次進入該方法的時候來設定兩個UILabel。

好了,現在假設傳入了4個字串@[@"這個是第0個字串",@"這個是第1個字串",@"這個是第2個字串",@"這個是第3個字串"];

只有兩個Label, 不管向右滾動還是向左滾動,我們將最初顯示的定為Labels[0], 後來顯示的定位Labels[1]

定義一個變數,即時的存放前一個UILabel的origin.X值,從0開始

1.每次前一個UILabel暫未完全隱藏前,後一個UIlabel就已經出現 (兩者間有一個固定的距離  internalWidth)

還需要根據speed的值更改Labels[0]的大小的增減來控制Labels[0]的位置(更改offsetX值)。

通過這個距離和前一個的位置則可以即時的計算後一個UILabel的位置(origin.X值)。

2.每次前一個UILabel完全隱藏時就需要重新設定一個值, 此時刻在每次前一個UILabel完全看不到後只進入一次

同時將左右兩個UILabel變換一下位置。 往左滾動: A<--B,A消失後,A跑到B右邊去了;   成為B<--A,B消失後,又要到A右邊去。

所以只需要設定offsetX = _labels[1].frame.origin.x;//A消失後,將後面的B位置作為下一個將消失的label的位置,A變為後面一個,

                               //其位置根據B的位置Realtime Compute出來。每次前一個消失後,如此迴圈的更換。

但是這樣只更改了位置,文本以及大小卻沒有變換,見3.

3.對於只有一個文本來說,AB的內容都是一樣的。但是對於傳入的四個字串而言,每次重新設定值的時候,需要更改AB內容。

同時,對於長度不等的字串,需要根據不同的文字大小來設定相應AB的Frame。

所以需將四個字串文字大小,常值內容在之前儲存為一個數組。定義一個始終記錄當前正準備消失的(前一個)UIlabel的位置:_currentIndex

在步驟1中: 從兩個數組中分別擷取用於顯示在A.B裡的文本數組:labelTextArray  frame數組:labelArray(從中取得寬和高)

每次AB位置交換的時候,需將currentIndex+1 : 即_currentIndex = (_currentIndex + 1)  % _textArray.count;以供交換後使用。

 

之後分別取得當前以及下一個的Text和frame   分別儲存到長度為2的數組   以便使用

 

上面太多、太亂。。。。。。我不想看我不想看我不想看。。。。。。

這裡有圖: 看完上面還完全不懂的請看這個吧。

 

再次解釋

<===============左移==================      

   

 

 

================右移=================>

現在只看顏色 看可以到 無論左滾右滾   綠色始終是Labels[0](將要消失的Label)  紅色始終是Label[1]

正常滾動情況下:  

  綠色的offsetX值隨著speed而變 : self.offsetX =  self.offsetX - sign * self.speed;

  紅色的X值會隨著綠色的offsetX和固定間距的關係而變 : CGFloat nextOffX = self.offsetX + sign * (((self.orientation == RollingOrientationLeft)? firstRect.size.width : lastRect.size.width) +   self.internalWidth);

  通過_currentIndex值從儲存到的資料中擷取到紅色、綠色的內容和大小後賦值:

當綠色消失的一瞬間:

  本該是在右邊的紅色一下子嚇綠了 : self.offsetX = _labels[1].frame.origin.x;  

  消失的綠色又將會按照正常滾動的情況下變為紅色

   _currentIndex指得始終是綠色內容的索引: _currentIndex = (_currentIndex + 1)  % _textArray.count;

  通過這個值又將會擷取按照正常滾動的情況下  紅色、綠色的大小和常值內容

  (兩個又將會進行的流程將會在"正常滾動情況下"的藍色部分操作)

    }

 

好了  解釋到此結束   看著好累。。。。   慢著   還有點擊事件沒寫完

點擊事件:在給UILabel添加了Tap手勢後進行處理

-(void)labelTap:(UITapGestureRecognizer *)gesture{    NSInteger tag = ((UILabel *)[gesture view]).tag - 100;    NSInteger index;    if(tag == 0){   //如果是(Labels[0])綠色        index = _currentIndex;    }else if (tag == 1){  //如果是(Labels[1])紅色  就是當前點擊的後一個        index = (_currentIndex + 1) % _textArray.count;    }else{          index = _currentIndex;    }   
if(self.labelClickBlock){ self.labelClickBlock(index); }}

 

終點

代碼見GitHub: ====>   YFRollingLabel       PS:源碼以及GitHub文檔都是用蹩腳的英語寫的,也不知道會不會有人看。。

另外說明記錄存在的問題:

對於放入的文本數組  長度不能太短   因為裡面只有兩個UILabel  如果長度太短的話   並且間距也小的情況下

在綠色剛消失後, 又會立馬變為紅色,出現在目前的綠色右邊,而不是慢慢的移動出現。

  

文本太長太長的話(幾百個中文,正常情況下不會設定這麼多吧), 會導致擷取的文本寬度過長,UILabel寬度過長,文本直接就不顯示了,但點擊事件還是有的  說明了還存在。。。這就搞不懂。。。

好了,終於寫完了,Windows 10 Mobile 萬歲!!!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.