在實際開發(fā)中太雨,經(jīng)常會遇到這幾種圖文排列情況
笨笨的我吩翻,第一次接到UI類似上圖下字設(shè)計時狭瞎,毫不猶豫的用了一個UILabel
和UIImageView
的組合,自己覺得很完美葫哗。但是隨著這種設(shè)計的地方越來越多球涛,就產(chǎn)生了很多重復(fù)代碼。
繼續(xù)笨笨的我想到了用個UIView
對上面封裝一下捺典,三下五除二完工从祝,代碼有所減少牍陌。
突然有一天,又來了第二種類似左圖右字的設(shè)計贮预。這是我已經(jīng)開始警醒了契讲,隨后將之前用UIView
封裝的那個類直接滿足了以上四種情況捡偏。
又過了不久,產(chǎn)品經(jīng)理告訴我你虹,把這個位置(類似上圖下字的地方)加個可以跳轉(zhuǎn)彤避,因為之前用的UIView
所以很自然的想到了添加一個手勢忠藤。楼雹。尖阔。
直到有一天介却,我在仿支付寶首頁時块茁,發(fā)現(xiàn)他的最頂上那一欄圖文是一體的(因為點擊圖片和文字都有高亮)数焊。然后上網(wǎng)查了查,原來是UIButton
實現(xiàn)的遂蛀。思路干厚,看了這種思路后蛮瞄,默默的回去重構(gòu)了之前的代碼。給UIButton
寫了個擴展,大概意思就是把imageEdgeInsets
芹助、titleEdgeInsets
封裝到一個方法里,支持上面的四種情況闲先,代碼又減少了一點饵蒂,似乎已經(jīng)很完美了。
就這樣又過了一段時間彼乌。UI給了一套大概這樣的九宮格設(shè)計慰照。
用UICollectionView
做布局琉朽,縫隙大小不對的同學(xué)可以看這里
看了一下箱叁,迅速用 UICollectionView
做出主體結(jié)構(gòu)惕医,然后每個cell
上面放上UIButton
抬伺,調(diào)用封裝的那個方法灾梦。Run一下若河。咦,字和圖片怎么有的是歪的捧灰,歪的统锤,檢查代碼饲窿,沒錯啊。思考一下阀溶,關(guān)于坐標的就只有imageEdgeInsets
,titleEdgeInsets
最終確定罪魁禍首是
imageEdgeInsets
,titleEdgeInsets
至于具體原因還不知道鸦泳,知道的同學(xué)告訴我一下做鹰。
解決
用
NSLayoutConstraint
約束圖片,文字繼續(xù)上面思路就好了(直接設(shè)置frame肯定是不行的)
extension UIButton {
func set(_ title: String?, with image: UIImage?, direction: NSLayoutAttribute = .top, interval: CGFloat = 10.0) {
setTitle(title, for: .normal)
setImage(image, for: .normal)
guard let titleSize = titleLabel?.bounds.size, let imageSize = imageView?.bounds.size else {
return
}
let horizontal = (frame.width - titleSize.width - imageSize.width - interval) / 2
let h = imageSize.height + interval
let vertical = (frame.height - titleSize.height - h) / 2
imageView?.translatesAutoresizingMaskIntoConstraints = false
if let constraints = imageView?.superview?.constraints {
imageView?.superview?.removeConstraints(constraints)
}
switch direction {
case .left, .right:
let centerY = NSLayoutConstraint(item: imageView!, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0)
let isRight = direction == .right
let constraint = NSLayoutConstraint(item: imageView!, attribute: direction, relatedBy: .equal, toItem: self, attribute: direction, multiplier: 1, constant: horizontal * (isRight ? -1 : 1))
imageView?.superview?.addConstraints([centerY, constraint])
let offsetX = isRight ? -(0.5 * interval + imageSize.width) * 2 : interval
titleEdgeInsets = UIEdgeInsets(top: 0, left: offsetX, bottom: 0, right: 0)
case .bottom, .top:
let value: CGFloat = direction == .top ? 1 : -1
let centerX = NSLayoutConstraint(item: imageView!, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0)
let constraint = NSLayoutConstraint(item: imageView!, attribute: direction, relatedBy: .equal, toItem: self, attribute: direction, multiplier: 1, constant: vertical * value)
imageView?.superview?.addConstraints([centerX, constraint])
titleEdgeInsets = UIEdgeInsets(top: h * value, left: -imageSize.width, bottom: 0, right: 0)
default:
fatalError("方向不匹配")
}
}
}
上面代碼不好理解的應(yīng)該是
let offsetX = isRight ? -(0.5 * interval + imageSize.width) * 2 : interval
在iPhone 5s上打印UIButton
的titleLabel
饭尝、imageView
、frame
假如什么什么也不做实撒,直接設(shè)置title
知态、image
的效果
結(jié)合上面打印的結(jié)果很容易得出imageView
的x
坐標
let imageViewX = (frame.width - imageView.frame.width - titleLabel.frame.width) / 2 = 41.25
因為1px=0.5pt肴甸,6p 3px=1pt囚巴,因為像素為最小單位彤叉,不可再分,所以系統(tǒng)取值41
知道以上公式后很容易得出
圖片在左邊浮庐,與文字間距
interval
(imageView的位置是NSLayoutConstraint約束定死的)
titleEdgeInsets = UIEdgeInsets(top: 0, left: interval, bottom: 0, right: 0)
圖片在右邊审残,與文字間距
interval
根據(jù)打印結(jié)果,應(yīng)該得出這個等式
let left = horizontal - titleLabel.frame.minX
titleEdgeInsets = UIEdgeInsets(top: 0, left: left, bottom: 0, right: 0)
效果是這樣的
計算結(jié)果是肯定沒錯的搅轿,根據(jù)我測量實際剛好差一半富玷,具體原因我也不知道
修改后
let left = horizontal - titleLabel.frame.minX
titleEdgeInsets = UIEdgeInsets(top: 0, left: left * 2, bottom: 0, right: 0)
但是這里還是有個問題,就是UICollectionViewCell
復(fù)用后雀鹃,titleLabel
的坐標又不對了黎茎,根據(jù)我打印titleLabel.frame.minX
是動態(tài)的
那只有想辦法得出一個靜態(tài)的titleLabel.frame.minX
根據(jù)以上打印結(jié)果当悔,可以推導(dǎo)出
let width = frame.width - imageView.frame.width - titleLabel.frame.width
titleLabel.frame.minX == width / 2 + imageView.frame.width // 全是靜態(tài)的
-0.5 * interval - imageSize.width == width / 2 - interval / 2 - titleLabel.frame.minX
titleEdgeInsets = UIEdgeInsets(top: 0, left: -0.5 * interval - imageSize.width, bottom: 0, right: 0)
以上問題完美解決