iOS根據寬高尋找最合適的字體大小(二)

iOS根據寬高尋找最合適的字體大小(二)

上一篇文章介紹了在寬高固定的情況下,尋找合適的字體大小使其可以單行完整顯示的計算方式,經試驗是可以滿足要求的,但是如果對於長字元串,單行顯示字體就變得很小,空間使用佔比小,更好的方式應該是可以多行顯示,盡量使文字填充整個空間。

對於這個需求,我在網上看到有一個網友給出的答案,先是定義一個字體,然後計算固定寬度情況下的高度,然後循環改變fontSize,使計算的高度搞好小於固定值,這個是一個思路,但是循環計算比較耗時,那有沒有其他的思路呢?可惜在網上還沒有看到其他的好方法。

本篇就來介紹一個本人實驗的一種方法,這個方法經過了實驗,是比較接近於最佳字體的方式,如果各位看了之後,有可改進之處,歡迎留言探討。下面開始介紹該計算方法。

解題的思路和前一篇一樣,尋找合適的字體比例因子,現在問題核心是如何確定這個比例因子。我的思路是,

1、確定M字體大小情況下,單個字元的寬高;

2、確定多少個字元可以剛好填充這個UILabel的空間;

3、確定字元串分Row行,Clum列;

4、求解除了單個字元的最佳寬和高;

5、和第1步計算出的寬高分別比較,取最小的那個值就是比例因子;

6、用M乘以該比例因子即是最合適的字體大小。

思路就是這樣,下面是我的具體實施過程。

#####確定M字體大小情況下,單個字元的寬高

可以使用一個字元調用`"國".boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)`,獲取到寬高,但是我在測試過程中發現,如果是英文或中英文混合時會出現比較大的偏差,所以我最後是採取整個字元串計算,然後使用寬度除以字元個數,取一個平均寬度值。如下:

```let constraintRect = CGSize(width: CGFloat(MAXFLOAT), height: rowHeight())let font = UIFont.init(name: info.fontName, size: 10) ?? UIFont.systemFont(ofSize: 10)var boundingBox = danmuText.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil) boundingBox.size.width = boundingBox.size.width / CGFloat(danmuText.count)```

#####確定多少個字元可以剛好填充這個UILabel的空間

這個問題換個表述就是,將一個長方形進行切分,怎樣切分得到的小長方形個數剛好大於等於M值,比如M=9,可以切分成3x3,也可以切分成1x9,2x5,3x4,這幾個值,那該怎麼選呢,我這裡採用的方法是,對M進行開方,sqrt(M) ≈ value1,然後取value2 = floor(value1)和value3 = ceil(M / value2),這樣得到value2 x value3是最接近M值得,可以認為value2行,value3列進行切分,那總可以填充的字元串就是value2 x value3個,代碼如下:

```//count表示字元總個數,取一個小,一個大是為了乘積結果可以剛好大於等於countlet count1 = CGFloat(count)let value1 = sqrt(count1)let value2 = floor(value1)let value3 = ceil(count1/value2)print("(value1) , (value2) , (value3)")```

#####確定字元串分Row行,Clum列

上面計算出來的結果值不一定滿足實際情況,如果情況是寬高比例比較懸殊的情況下,合適的切分是1 x 9 而不是 3 x 3就需要考慮了,所以需要對上面計算出的結果值進行一次校對,那如何校對呢?我的辦法是使其行列的比例接近於實際的寬高比,計算方式如下:

```let rr = realSize.width / realSize.heightvar row: CGFloat = 0.0var clum: CGFloat = 0.0 //公式: row * (rr * row) = value2 * value3 row = sqrt(CGFloat(value2 * value3) / rr) clum = rr * row```

上面得到的行列值,還需要進行校正,首先行列不能小於1,其次必須是整數,還是取一個floor,一個ceil,這樣得到的值可能和value2 x value3的個數不吻合,大於沒問題,小於則會導致字元顯示不全,所以還需要根據寬高比例進行調整,如果寬大於高,那麼就列大於等於行,如果高大於寬,那麼久行大於等於列。代碼如下:

```//不能小於1row = row > 1 ? row : 1clum = clum > 1 ? clum : 1 //判斷寬高比是不是更好,寬大於高,增加列,高大於寬,增加行//寬大於高if rr >= 1 { row = floor(row) clum = ceil(clum) if row * clum < value2 * value3 { let min = row > clum ? clum : row row = min clum = ceil(value2 * value3 / min) } } else { row = ceil(row) clum = floor(clum) if row * clum < value2 * value3 { let min = row > clum ? clum : row clum = min row = ceil(value2 * value3 / min) }}```

#####求解除了單個字元的最佳寬和高

上一步得到了row和clum值,這一步就簡單了,用實際寬高進行計算即可,代碼如下:

```let width = realSize.width / CGFloat(clum)let height = realSize.height / CGFloat(row) let wordSize = CGSize(width: width, height: height)```

#####和第1步計算出的寬高分別比較,取最小的那個值就是比例因子

這一步也比較簡單,直接看代碼,取最小的那個比例值:

```let rate1 = wordSize.width / boundingBox.widthlet rate2 = wordSize.height / boundingBox.heightvar rate = rate1if rate1 > rate2 { rate = rate2}```

#####用M乘以該比例因子即是最合適的字體大小

獲取到了rate值,`itemView.font = UIFont.systemFont(ofSize: size)`,重新設置UILabel的值或者`attributedText`即可。

下面是我實驗的實際效果:

####後記

前面已經說過,上面的方法得到的結果是比較接近於理想值,但是與理想值還是有一點差距的,主要是確定row和clum時會有偏差,比如M=5,在我這種寬高比例情況下得到的row=1,clum=6,而不是1 x 5,所以導致fontSize並不是最合適的,但是在考慮性能時,這是目前想到的比較理想的方法了,如果各位有新的方法,歡迎留言探討。

原創文章,轉載請告知作者,並標明出處和作者。


推薦閱讀:

TAG:字體大小 | 字體 | iOS |