Raf*_*fAl 100
或者你可以使用这个类别:
@interface UIButton (VerticalLayout)
- (void)centerVerticallyWithPadding:(float)padding;
- (void)centerVertically;
@end
@implementation UIButton (VerticalLayout)
- (void)centerVerticallyWithPadding:(float)padding {
CGSize imageSize = self.imageView.frame.size;
CGSize titleSize = self.titleLabel.frame.size;
CGFloat totalHeight = (imageSize.height + titleSize.height + padding);
self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
0.0f,
0.0f,
- titleSize.width);
self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
- imageSize.width,
- (totalHeight - titleSize.height),
0.0f);
self.contentEdgeInsets = UIEdgeInsetsMake(0.0f,
0.0f,
titleSize.height,
0.0f);
}
- (void)centerVertically {
const CGFloat kDefaultPadding = 6.0f;
[self centerVerticallyWithPadding:kDefaultPadding];
}
@end
Run Code Online (Sandbox Code Playgroud)
extension UIButton {
func centerVertically(padding: CGFloat = 6.0) {
guard
let imageViewSize = self.imageView?.frame.size,
let titleLabelSize = self.titleLabel?.frame.size else {
return
}
let totalHeight = imageViewSize.height + titleLabelSize.height + padding
self.imageEdgeInsets = UIEdgeInsets(
top: -(totalHeight - imageViewSize.height),
left: 0.0,
bottom: 0.0,
right: -titleLabelSize.width
)
self.titleEdgeInsets = UIEdgeInsets(
top: 0.0,
left: -imageViewSize.width,
bottom: -(totalHeight - titleLabelSize.height),
right: 0.0
)
self.contentEdgeInsets = UIEdgeInsets(
top: 0.0,
left: 0.0,
bottom: titleLabelSize.height,
right: 0.0
)
}
}
Run Code Online (Sandbox Code Playgroud)
Chr*_*ris 87
在Xcode中,您只需将Edge Title Left Inset设置为负图像宽度即可.这将在图像的中心显示标签.
要使标签显示在图像下方(按应用按钮排序),您可能需要将Edge Title Top Inset设置为某个正数.
sim*_*eon 47
这是一个简单的居中标题按钮,通过覆盖titleRect(forContentRect:)和在Swift中实现imageRect(forContentRect:).它还实现intrinsicContentSize了与AutoLayout一起使用.
import UIKit
class CenteredButton: UIButton
{
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.titleRect(forContentRect: contentRect)
return CGRect(x: 0, y: contentRect.height - rect.height + 5,
width: contentRect.width, height: rect.height)
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
let titleRect = self.titleRect(forContentRect: contentRect)
return CGRect(x: contentRect.width/2.0 - rect.width/2.0,
y: (contentRect.height - titleRect.height)/2.0 - rect.height/2.0,
width: rect.width, height: rect.height)
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
if let image = imageView?.image {
var labelHeight: CGFloat = 0.0
if let size = titleLabel?.sizeThatFits(CGSize(width: self.contentRect(forBounds: self.bounds).width, height: CGFloat.greatestFiniteMagnitude)) {
labelHeight = size.height
}
return CGSize(width: size.width, height: image.size.height + labelHeight + 5)
}
return size
}
override init(frame: CGRect) {
super.init(frame: frame)
centerTitleLabel()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
centerTitleLabel()
}
private func centerTitleLabel() {
self.titleLabel?.textAlignment = .center
}
}
Run Code Online (Sandbox Code Playgroud)
Tia*_*ago 38
看看Swift 中的这个好答案.
extension UIButton {
func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
let imageSize = self.imageView!.frame.size
let titleSize = self.titleLabel!.frame.size
let totalHeight = imageSize.height + titleSize.height + padding
self.imageEdgeInsets = UIEdgeInsets(
top: -(totalHeight - imageSize.height),
left: 0,
bottom: 0,
right: -titleSize.width
)
self.titleEdgeInsets = UIEdgeInsets(
top: 0,
left: -imageSize.width,
bottom: -(totalHeight - titleSize.height),
right: 0
)
}
}
Run Code Online (Sandbox Code Playgroud)
Dav*_*ton 35
子类UIButton.覆盖 - layoutSubviews将内置移动subviews到新位置:
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect frame = self.imageView.frame;
frame = CGRectMake(truncf((self.bounds.size.width - frame.size.width) / 2), 0.0f, frame.size.width, frame.size.height);
self.imageView.frame = frame;
frame = self.titleLabel.frame;
frame = CGRectMake(truncf((self.bounds.size.width - frame.size.width) / 2), self.bounds.size.height - frame.size.height, frame.size.width, frame.size.height);
self.titleLabel.frame = frame;
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*bin 21
纠正了其中一个答案:
斯威夫特3:
class CenteredButton: UIButton
{
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.titleRect(forContentRect: contentRect)
let imageRect = super.imageRect(forContentRect: contentRect)
return CGRect(x: 0, y: imageRect.maxY + 10,
width: contentRect.width, height: rect.height)
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
let titleRect = self.titleRect(forContentRect: contentRect)
return CGRect(x: contentRect.width/2.0 - rect.width/2.0,
y: (contentRect.height - titleRect.height)/2.0 - rect.height/2.0,
width: rect.width, height: rect.height)
}
override init(frame: CGRect) {
super.init(frame: frame)
centerTitleLabel()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
centerTitleLabel()
}
private func centerTitleLabel() {
self.titleLabel?.textAlignment = .center
}
}
Run Code Online (Sandbox Code Playgroud)
Kam*_*icz 20
重构icecrystal23的答案.
Swift 3,适用于自动布局,xib,故事板,可以动画.
原始icecrystal23的答案中的按钮有一个严重计算的框架.我想我已经修好了.
import UIKit
@IBDesignable
class VerticalButton: UIButton {
@IBInspectable public var padding: CGFloat = 20.0 {
didSet {
setNeedsLayout()
}
}
override var intrinsicContentSize: CGSize {
let maxSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
if let titleSize = titleLabel?.sizeThatFits(maxSize), let imageSize = imageView?.sizeThatFits(maxSize) {
let width = ceil(max(imageSize.width, titleSize.width))
let height = ceil(imageSize.height + titleSize.height + padding)
return CGSize(width: width, height: height)
}
return super.intrinsicContentSize
}
override func layoutSubviews() {
if let image = imageView?.image, let title = titleLabel?.attributedText {
let imageSize = image.size
let titleSize = title.size()
titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + padding), right: 0.0)
imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + padding), left: 0.0, bottom: 0.0, right: -titleSize.width)
}
super.layoutSubviews()
}
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*k W 16
如果你继承UIButton和override layoutSubviews,你可以用下面的居中图像和居中放置在它下面的标题:
kTextTopPadding 是你必须引入的一个常量,它决定了图像和它下面文本之间的空间.
-(void)layoutSubviews {
[super layoutSubviews];
// Move the image to the top and center it horizontally
CGRect imageFrame = self.imageView.frame;
imageFrame.origin.y = 0;
imageFrame.origin.x = (self.frame.size.width / 2) - (imageFrame.size.width / 2);
self.imageView.frame = imageFrame;
// Adjust the label size to fit the text, and move it below the image
CGRect titleLabelFrame = self.titleLabel.frame;
CGSize labelSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font
constrainedToSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)
lineBreakMode:NSLineBreakByWordWrapping];
titleLabelFrame.size.width = labelSize.width;
titleLabelFrame.size.height = labelSize.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
titleLabelFrame.origin.y = self.imageView.frame.origin.y + self.imageView.frame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame;
}
Run Code Online (Sandbox Code Playgroud)
Ken*_*ker 16
这是Erik W优秀答案的修改版本.但是,不是将图像放在视图的顶部,而是将图像和标签作为一组放在视图中心.
不同之处是:
+-----------+
| ( ) |
| Hello | // Erik W's code
| |
| |
+-----------+
Run Code Online (Sandbox Code Playgroud)
VS
+-----------+
| |
| ( ) | // My modified version
| Hello |
| |
+-----------+
Run Code Online (Sandbox Code Playgroud)
来源如下:
-(void)layoutSubviews {
[super layoutSubviews];
CGRect titleLabelFrame = self.titleLabel.frame;
CGSize labelSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
CGRect imageFrame = self.imageView.frame;
CGSize fitBoxSize = (CGSize){.height = labelSize.height + kTextTopPadding + imageFrame.size.height, .width = MAX(imageFrame.size.width, labelSize.width)};
CGRect fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize.height)/2);
imageFrame.origin.y = fitBoxRect.origin.y;
imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
self.imageView.frame = imageFrame;
// Adjust the label size to fit the text, and move it below the image
titleLabelFrame.size.width = labelSize.width;
titleLabelFrame.size.height = labelSize.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame;
}
Run Code Online (Sandbox Code Playgroud)
仅供参考:当与UIView动画结合使用时,这可能会中断,因为在它们期间会调用layoutSubviews.
Bar*_*nas 15
戴夫在Swift中的解决方案:
override func layoutSubviews() {
super.layoutSubviews()
if let imageView = self.imageView {
imageView.frame.origin.x = (self.bounds.size.width - imageView.frame.size.width) / 2.0
imageView.frame.origin.y = 0.0
}
if let titleLabel = self.titleLabel {
titleLabel.frame.origin.x = (self.bounds.size.width - titleLabel.frame.size.width) / 2.0
titleLabel.frame.origin.y = self.bounds.size.height - titleLabel.frame.size.height
}
}
Run Code Online (Sandbox Code Playgroud)
更新了Kenny Winker的答案,因为sizeWithFont在iOS 7中已被弃用.
-(void)layoutSubviews {
[super layoutSubviews];
int kTextTopPadding = 3;
CGRect titleLabelFrame = self.titleLabel.frame;
CGRect labelSize = [self.titleLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds)) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.titleLabel.font} context:nil];
CGRect imageFrame = self.imageView.frame;
CGSize fitBoxSize = (CGSize){.height = labelSize.size.height + kTextTopPadding + imageFrame.size.height, .width = MAX(imageFrame.size.width, labelSize.size.width)};
CGRect fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize.height)/2);
imageFrame.origin.y = fitBoxRect.origin.y;
imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
self.imageView.frame = imageFrame;
// Adjust the label size to fit the text, and move it below the image
titleLabelFrame.size.width = labelSize.size.width;
titleLabelFrame.size.height = labelSize.size.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.size.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame;
}
Run Code Online (Sandbox Code Playgroud)
小智 7
在iOS 11 / Swift 4上,以上答案均对我无济于事。我找到了一些例子,并对此进行了介绍:
extension UIButton {
func centerImageAndButton(_ gap: CGFloat, imageOnTop: Bool) {
guard let imageView = self.currentImage,
let titleLabel = self.titleLabel?.text else { return }
let sign: CGFloat = imageOnTop ? 1 : -1
self.titleEdgeInsets = UIEdgeInsetsMake((imageView.size.height + gap) * sign, -imageView.size.width, 0, 0);
let titleSize = titleLabel.size(withAttributes:[NSAttributedStringKey.font: self.titleLabel!.font!])
self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + gap) * sign, 0, 0, -titleSize.width)
}
}
Run Code Online (Sandbox Code Playgroud)
希望这对某人有帮助。
小智 6
使用Kenny Winker和simeon的代码,我制作了适合我的快速代码.
import UIKit
@IBDesignable
class TopIconButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
let kTextTopPadding:CGFloat = 3.0;
var titleLabelFrame = self.titleLabel!.frame;
let labelSize = titleLabel!.sizeThatFits(CGSizeMake(CGRectGetWidth(self.contentRectForBounds(self.bounds)), CGFloat.max))
var imageFrame = self.imageView!.frame;
let fitBoxSize = CGSizeMake(max(imageFrame.size.width, labelSize.width), labelSize.height + kTextTopPadding + imageFrame.size. height)
let fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize. height)/2);
imageFrame.origin.y = fitBoxRect.origin.y;
imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
self.imageView!.frame = imageFrame;
// Adjust the label size to fit the text, and move it below the image
titleLabelFrame.size.width = labelSize.width;
titleLabelFrame.size.height = labelSize.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel!.frame = titleLabelFrame;
self.titleLabel!.textAlignment = .Center
}
}
Run Code Online (Sandbox Code Playgroud)
Swift 5 - 以下方法对我有用
func centerVerticallyWithPadding(padding : CGFloat) {
guard
let imageViewSize = self.imageView?.frame.size,
let titleLabelSize = self.titleLabel?.frame.size else {
return
}
let totalHeight = imageViewSize.height + titleLabelSize.height + padding
self.imageEdgeInsets = UIEdgeInsets(
top: max(0, -(totalHeight - imageViewSize.height)),
left: 0.0,
bottom: 0.0,
right: -titleLabelSize.width
)
self.titleEdgeInsets = UIEdgeInsets(
top: (totalHeight - imageViewSize.height),
left: -imageViewSize.width,
bottom: -(totalHeight - titleLabelSize.height),
right: 0.0
)
self.contentEdgeInsets = UIEdgeInsets(
top: 0.0,
left: 0.0,
bottom: titleLabelSize.height,
right: 0.0
)
}
Run Code Online (Sandbox Code Playgroud)
确保您的按钮标题没有在故事板/xib 中被截断,否则请使用
解决方案 2
class SVVerticalButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
let padding : CGFloat = 2.0
if let imageView = self.imageView {
imageView.frame.origin.x = (self.bounds.size.width - imageView.frame.size.width) / 2.0
imageView.frame.origin.y = max(0,(self.bounds.size.height - (imageView.frame.size.height + (titleLabel?.frame.size.height ?? 0.0) + padding)) / 2.0)
}
if let titleLabel = self.titleLabel {
titleLabel.frame.origin.x = 0
titleLabel.frame.origin.y = self.bounds.size.height - titleLabel.frame.size.height
titleLabel.frame.size.width = self.bounds.size.width
titleLabel.textAlignment = .center
}
}
}
Run Code Online (Sandbox Code Playgroud)
Swift 5,自动布局
我实现了一些方法的组合,这对我来说效果最好。技巧在于覆盖titleRect(forContentRect:)、imageRect(forContentRect:)方法和intrinsicContentSize这些视图的计算值。
结果:
final class CustomButton: UIButton {
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
let superRect = super.titleRect(forContentRect: contentRect)
return CGRect(
x: 0,
y: contentRect.height - superRect.height,
width: contentRect.width,
height: superRect.height
)
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
let superRect = super.imageRect(forContentRect: contentRect)
return CGRect(
x: contentRect.width / 2 - superRect.width / 2,
y: (contentRect.height - titleRect(forContentRect: contentRect).height) / 2 - superRect.height / 2,
width: superRect.width,
height: superRect.height
)
}
override var intrinsicContentSize: CGSize {
_ = super.intrinsicContentSize
guard let image = imageView?.image else { return super.intrinsicContentSize }
let size = titleLabel?.sizeThatFits(contentRect(forBounds: bounds).size) ?? .zero
let spacing: CGFloat = 12
return CGSize(width: max(size.width, image.size.width), height: image.size.height + size.height + spacing)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
titleLabel?.textAlignment = .center
}
}
Run Code Online (Sandbox Code Playgroud)
上述解决方案可能不适用于 iOS 12 及更早版本。在这种情况下,根据字体大小创建具有固定图像大小和标签高度的常量。
final class CustomButton: UIButton {
private enum Constants {
static let imageSize: CGFloat = 48
static let titleHeight = yourFont.pointSize - yourFont.descender
}
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
_ = super.titleRect(forContentRect: contentRect)
return CGRect(
x: 0,
y: contentRect.height - Constants.titleHeight,
width: contentRect.width,
height: Constants.titleHeight
)
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
return CGRect(
x: contentRect.width / 2 - Constants.imageSize / 2,
y: (contentRect.height - titleRect(forContentRect: contentRect).height) / 2 - Constants.imageSize / 2,
width: Constants.imageSize,
height: Constants.imageSize
)
}
override var intrinsicContentSize: CGSize {
_ = super.intrinsicContentSize
let size = titleLabel?.sizeThatFits(contentRect(forBounds: bounds).size) ?? .zero
let spacing: CGFloat = 12
return CGSize(
width: max(size.width, Constants.imageSize),
height: Constants.imageSize + Constants.titleHeight + spacing
)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
titleLabel?.textAlignment = .center
}
}
Run Code Online (Sandbox Code Playgroud)
@Tiago 我像这样改变你的答案。它适用于所有尺寸
func alignImageAndTitleVertically(padding: CGFloat = 5.0) {
self.sizeToFit()
let imageSize = self.imageView!.frame.size
let titleSize = self.titleLabel!.frame.size
let totalHeight = imageSize.height + titleSize.height + padding
self.imageEdgeInsets = UIEdgeInsets(
top: -(totalHeight - imageSize.height),
left: 0,
bottom: 0,
right: -titleSize.width
)
self.titleEdgeInsets = UIEdgeInsets(
top: 0,
left: 0,
bottom: -(totalHeight - titleSize.height),
right: titleSize.height
)
}
Run Code Online (Sandbox Code Playgroud)
本地化友好的解决方案:
有很多很棒的解决方案,但我想在这里为那些使用本地化的人添加一条注释。
您需要反转左右 EdgeInstets 值,以便在语言方向从 LtR 更改为 RtL 时正确布置按钮。
使用类似的解决方案,我将按如下方式实现它:
extension UIButton {
func alignVertical(spacing: CGFloat, lang: String) {
guard let imageSize = self.imageView?.image?.size,
let text = self.titleLabel?.text,
let font = self.titleLabel?.font
else { return }
let labelString = NSString(string: text)
let titleSize = labelString.size(
withAttributes: [NSAttributedString.Key.font: font]
)
var titleLeftInset: CGFloat = -imageSize.width
var titleRigtInset: CGFloat = 0.0
var imageLeftInset: CGFloat = 0.0
var imageRightInset: CGFloat = -titleSize.width
if Locale.current.languageCode! != "en" { // If not Left to Right language
titleLeftInset = 0.0
titleRigtInset = -imageSize.width
imageLeftInset = -titleSize.width
imageRightInset = 0.0
}
self.titleEdgeInsets = UIEdgeInsets(
top: 0.0,
left: titleLeftInset,
bottom: -(imageSize.height + spacing),
right: titleRigtInset
)
self.imageEdgeInsets = UIEdgeInsets(
top: -(titleSize.height + spacing),
left: imageLeftInset,
bottom: 0.0,
right: imageRightInset
)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
self.contentEdgeInsets = UIEdgeInsets(
top: edgeOffset,
left: 0.0,
bottom: edgeOffset,
right: 0.0
)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
81707 次 |
| 最近记录: |