UIButton üzerindeki metin ve görüntüyü imageEdgeInsets ve titleEdgeInsets ile hizalama


250

Görüntü ile metnin başlangıcı arasında yaklaşık 2-3 piksel boşluk olacak şekilde metnin iki satırının soluna bir simge yerleştirmek istiyorum. Kontrolün kendisi yatay olarak Merkez hizalanır (Arayüz Oluşturucu ile ayarlanır)

Düğme şuna benzer:

|                  |
|[Image] Add To    |
|        Favorites |

Bu boşuna contentEdgeInset, imageEdgeInsets ve titleEdgeInsets ile yapılandırmaya çalışıyorum. Negatif bir değerin kenarı genişlettiğini, pozitif bir değer ise merkeze yaklaştıkça küçüldüğünü anlıyorum.

Denedim:

[button setTitleEdgeInsets:UIEdgeInsetsMake(0, -image.size.width, 0, 0)];
[button setImageEdgeInsets:UIEdgeInsetsMake(0, button.titleLabel.bounds.size.width, 0, 0)];

ancak bu düzgün görüntülenmiyor. Ben değerleri tweaking ama sol iç metin değeri -5 den -10 demek beklenen şekilde taşımak için görünmüyor. -10 Metni tamamen sola doğru çeker, böylece -5'i sol taraftan yarıya atmayı beklerdim ama yapmaz.

İç metinlerin ardındaki mantık nedir? Görüntü yerleşimlerini ve ilgili terminolojiyi bilmiyorum.

Bu SO sorusunu referans olarak kullandım, ancak değerlerimle ilgili bir şey doğru değil. UIButton: imageEdgeInsets ve titleEdgeInsets kullanarak bir görüntüyü ve metni nasıl ortalarsınız?

Yanıtlar:


392

İle ilgili belgelere katılıyorum imageEdgeInsetsvetitleEdgeInsets daha iyi olmalı, ancak deneme yanılma başvurusu yapmadan doğru pozisyonu nasıl alacağımı anladım.

Genel fikir bu soruda , ancak hem metin hem de görüntüyü ortalamak isteseydiniz. Görüntünün ve metnin ayrı ayrı ortalanmasını istemiyoruz, görüntünün ve metnin tek bir varlık olarak birlikte ortalanmasını istiyoruz. Bu aslında UIButton'un zaten yaptığı şeydir, bu yüzden sadece aralığı ayarlamamız gerekir.

CGFloat spacing = 10; // the amount of spacing to appear between image and title
tabBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
tabBtn.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);

Ayrıca bunu UIButton için bir kategoriye çevirdim, bu yüzden kullanımı kolay olacak:

UIButton + Position.h

@interface UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;

@end

UIButton + Position.m

@implementation UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing {
    self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);
}

@end

Şimdi tek yapmam gereken:

[button centerButtonAndImageWithSpacing:10];

Ve her zaman ihtiyacım olanı alıyorum. Artık kenar iç metinleri ile karışıklık yok.

EDIT: Görüntü ve Metin Değiştirme

Yorumlarda @ Javal adlı kişiye yanıt olarak

Aynı mekanizmayı kullanarak görüntüyü ve metni değiştirebiliriz. Değiştirmeyi gerçekleştirmek için, negatif bir boşluk kullanın, ancak metnin ve görüntünün genişliğini de ekleyin. Bu, çerçevelerin bilinmesini ve düzenin önceden gerçekleştirilmesini gerektirir.

[self.view layoutIfNeeded];
CGFloat flippedSpacing = -(desiredSpacing + button.currentImage.size.width + button.titleLabel.frame.size.width);
[button centerButtonAndImageWithSpacing:flippedSpacing];

Tabii ki muhtemelen bunun için güzel bir yöntem yapmak isteyeceksiniz, potansiyel olarak ikinci bir kategori yöntemi ekleyeceksiniz, bu okuyucuya bir alıştırma olarak bırakılmıştır.


Normal ve vurgulananlar için farklı başlıklar varsa, kullanıcı düğmeyi vurgulayıp işaretini kaldırdığında nasıl yeniden ortalayabilirim?
user102008 14:12

@ user102008 Tamamen farklı başlıklar? Yoksa sadece farklı renkler? Kullanıyorsanız [UIButton setTitleColor:forState:]veya hatta kullanıyorsanız konumlandırma değişmemelidir [UIButton setTitle:forState:].
Kekoa

5
[Düğme sizeToFit] 'i düzgün şekilde boyutlandıracak şekilde nasıl düzeltirsiniz?
RonLugge

@RonLugge neden sizeToFit'e ihtiyacınız olduğundan emin değilim, bu yüzden sorunuzu cevaplayamıyorum. SizeToFit kullanmadan benim için iyi çalışıyor.
Kekoa

Düğmeler / metin dinamik olduğundan sizeToFit gerekir, bu yüzden (kullanıcı tanımlı) etiketine sığdırmak için boyutlandırmak gerekir. Sorun, eklenen alanı telafi etmemesidir. Geçersiz
kıldım

397

Bu partiye biraz geç kaldım, ama eklemek için faydalı bir şeyim olduğunu düşünüyorum.

Kekoa'nın cevabı harika, ancak RonLugge'dan bahsettiği gibi, düğmenin artık saygı duymamasını sağlayabilir sizeToFitveya daha da önemlisi, düğmenin kendiliğinden boyutlandırıldığında içeriğinin kırpılmasına neden olabilir. Olmadı!

İlk olarak,

Nasıl inandığımın kısa bir açıklaması imageEdgeInsets ve titleEdgeInsetsçalıştığımın :

İçin dokümanlarimageEdgeInsets kısmen söylemek gerekirse:

Düğme görüntüsü için etkili çizim dikdörtgenini yeniden boyutlandırmak ve yeniden konumlandırmak için bu özelliği kullanın. Dört iç metin (her biri için üst, sol, alt, sağ) için farklı bir değer belirleyebilirsiniz. Pozitif bir değer o kenarı küçültür veya ekler — düğmenin ortasına yaklaştırarak. Negatif bir değer bu kenarı genişletir veya öteler.

Bu belgenin, düğmenin başlığı olmadığını, sadece bir resmi olduğunu hayal ederek yazıldığına inanıyorum. Bu şekilde düşünmek çok daha mantıklı ve UIEdgeInsetsgenellikle nasıl davranıyor . Temel olarak, görüntünün çerçevesi (veya başlığı ile titleEdgeInsets) pozitif iç metinler için içe, negatif metinler için dışa doğru hareket ettirilir.

Tamam, ne olmuş yani?

Oraya geliyorum! Varsayılan olarak, bir görüntü ve bir başlık ayarlayarak aşağıdakilere sahip olursunuz (düğme kenarlığı sadece nerede olduğunu göstermek için yeşildir):

Başlangıç ​​resmi;  başlık ve resim arasında boşluk yok.

Bir görüntü ile bir başlık arasında boşluk olmasını istediğinizde, bunların herhangi birinin ezilmesine neden olmadan, görüntünün ve başlığın her birinde iki tane olmak üzere dört farklı ek ayarlamanız gerekir. Bunun nedeni , bu öğelerin çerçevelerinin boyutlarını değil, yalnızca konumlarını değiştirmek istemenizdir . Bu şekilde düşünmeye başladığınızda, Kekoa'nın mükemmel kategorisinde gereken değişiklik netleşir:

@implementation UIButton(ImageTitleCentering)

- (void)centerButtonAndImageWithSpacing:(CGFloat)spacing {
    CGFloat insetAmount = spacing / 2.0;
    self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount);
}

@end

Ama bekle , diyorsun ki, bunu yaptığımda şunu anlıyorum:

Aralık iyi, ancak görüntü ve başlık görünümün çerçevesinin dışında.

Ah evet! Unuttum, dokümanlar beni bu konuda uyardı. Kısmen diyorlar:

Bu özellik, yalnızca mizanpaj sırasında görüntüyü konumlandırmak için kullanılır. Düğmesine belirlemek için bu özelliği kullanmaz intrinsicContentSizeve sizeThatFits:.

Ama bir kutu yardımıyla bir özellik ve bu en contentEdgeInsets. Bunun için dokümanlar kısmen:

Düğmesine belirlemek için bu özelliğini kullanır intrinsicContentSizeve sizeThatFits:.

Kulağa hoş geliyor. Kategoriyi bir kez daha değiştirelim:

@implementation UIButton(ImageTitleCentering)

- (void)centerButtonAndImageWithSpacing:(CGFloat)spacing {
    CGFloat insetAmount = spacing / 2.0;
    self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount);
    self.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount);
}

@end

Ve ne elde edersiniz?

Aralık ve çerçeve artık doğru.

Bana bir kazanan gibi geliyor.


Swift'te çalışıyorsunuz ve hiç düşünmek istemiyor musunuz? Swift'teki uzantının son sürümü:

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount, bottom: 0, right: insetAmount)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: -insetAmount)
        contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

25
Ne harika bir cevap! Evet, partiye birkaç yıl geç kaldı, ancak bu intrinsicContentSize, orijinal yanıtın kabul edilmesinden bu yana bu otomatik yerleşim günlerinde çok önemli olan yanlış olma sorununu çözdü .
Acey

2
Düğmenin dışı ile görüntü ve etiket arasında aynı miktarda boşluk spacingistiyorsanız, self.contentEdgeInsets'in dört değerinin her birine ekleyin :self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
Erik van der Neut

1
Harika cevap, görüntü doğru hizalandığında ve metin uzunluğu değişebildiğinde çok iyi çalışmıyor.
jlpiedrahita

2
Neredeyse mükemmel cevap! Yalnızca eksik olan şey, görüntü ve başlık girişlerinin Sağdan Sola arabirimde çalıştığında ters çevrilmesi gerektiğidir.
jeeeyul

görüntü oranının 1'e 1 olarak ayarlanması
Yestay Muratov

39

Arayüz Oluşturucu'da. UIButton -> Nitelikler Denetçisi -> Kenar = Başlık'ı seçin ve kenar iç metinlerini değiştirin


38

Ayrıca benzer bir şey yapmak istiyorsanız

resim açıklamasını buraya girin

İhtiyacın var

1. düğmesi için yatay ve dikey hizalamayı ayarlayın

resim açıklamasını buraya girin

  1. Gerekli tüm değerleri bulun ve ayarlayın UIImageEdgeInsets

            CGSize buttonSize = button.frame.size;
            NSString *buttonTitle = button.titleLabel.text;
            CGSize titleSize = [buttonTitle sizeWithAttributes:@{ NSFontAttributeName : [UIFont camFontZonaProBoldWithSize:12.f] }];
            UIImage *buttonImage = button.imageView.image;
            CGSize buttonImageSize = buttonImage.size;
    
            CGFloat offsetBetweenImageAndText = 10; //vertical space between image and text
    
            [button setImageEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 - offsetBetweenImageAndText,
                                                        (buttonSize.width - buttonImageSize.width) / 2,
                                                        0,0)];                
            [button setTitleEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 + buttonImageSize.height + offsetBetweenImageAndText,
                                                        titleSize.width + [button imageEdgeInsets].left > buttonSize.width ? -buttonImage.size.width  +  (buttonSize.width - titleSize.width) / 2 : (buttonSize.width - titleSize.width) / 2 - buttonImage.size.width,
                                                        0,0)];

Bu düğme üzerindeki başlığınızı ve resminizi düzenler.

Ayrıca her geçişte bunu güncellediğini lütfen unutmayın


hızlı

import UIKit

extension UIButton {
    // MARK: - UIButton+Aligment

    func alignContentVerticallyByCenter(offset:CGFloat = 10) {
        let buttonSize = frame.size

        if let titleLabel = titleLabel,
            let imageView = imageView {

            if let buttonTitle = titleLabel.text,
                let image = imageView.image {
                let titleString:NSString = NSString(string: buttonTitle)
                let titleSize = titleString.sizeWithAttributes([
                    NSFontAttributeName : titleLabel.font
                    ])
                let buttonImageSize = image.size

                let topImageOffset = (buttonSize.height - (titleSize.height + buttonImageSize.height + offset)) / 2
                let leftImageOffset = (buttonSize.width - buttonImageSize.width) / 2
                imageEdgeInsets = UIEdgeInsetsMake(topImageOffset,
                                                   leftImageOffset,
                                                   0,0)

                let titleTopOffset = topImageOffset + offset + buttonImageSize.height
                let leftTitleOffset = (buttonSize.width - titleSize.width) / 2 - image.size.width

                titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset,
                                                   leftTitleOffset,
                                                   0,0)
            }
        }
    }
}

29

Bunu kullanarak fazla sorundan kaçınabilirsiniz -

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;

Bu, tüm içeriğinizi otomatik olarak sola (veya istediğiniz yere) hizalar

Hızlı 3:

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;

myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center; //
Yazım hatası

25

In Xcode 8.0 basitçe değiştirerek bunu yapabilirsinizinsets boyut denetiminde.

UIButton'u seçin -> Özellik Denetçisi -> boyut denetçisine gidin ve içerik, görüntü ve başlık iç metinlerini değiştirin.

resim açıklamasını buraya girin

Ve sağ taraftaki görüntüyü değiştirmek isterseniz, anlamsal özelliği Force Right-to-leftÖzellik denetçisinde olarak değiştirebilirsiniz.

resim açıklamasını buraya girin


benim durumumda düğme resmi asla xcode 10 ile metnin sağ tarafına geçmez? Yardım edebilir misin?
Satish Mavani

Merhaba Satish, bu da xcode 10 ile iyi çalışıyor. Umarım görüntü arka plan görüntüsü değil ayarlıyorsunuz ve ayrıca boyut denetçisini kullanarak görüntü böceklerini değiştirebilirsiniz.
Sahil

18

Ben de bu partiye biraz geç kaldım, ama eklemek için faydalı bir şeyim olduğunu düşünüyorum: o).

UIButtonAmacı, düğmenin görüntüsünün dikey ya da yatay olarak nerede olacağını seçmek olan bir alt sınıf oluşturdum .

Bu, bu tür düğmeler yapabileceğiniz anlamına gelir: farklı tür düğmeler

Sınıfımla bu düğmelerin nasıl oluşturulacağıyla ilgili ayrıntılar:

func makeButton (imageVerticalAlignment:LayoutableButton.VerticalAlignment, imageHorizontalAlignment:LayoutableButton.HorizontalAlignment, title:String) -> LayoutableButton {
    let button = LayoutableButton ()

    button.imageVerticalAlignment = imageVerticalAlignment
    button.imageHorizontalAlignment = imageHorizontalAlignment

    button.setTitle(title, for: .normal)

    // add image, border, ...

    return button
}

let button1 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .left, title: "button1")
let button2 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .right, title: "button2")
let button3 = makeButton(imageVerticalAlignment: .top, imageHorizontalAlignment: .center, title: "button3")
let button4 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button4")
let button5 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button5")
button5.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

Bunu yapmak için 2 özellik ekledim: imageVerticalAlignmentve imageHorizontalAlignment. Elbette, Düğmenizde sadece bir resim veya başlık varsa ... bu sınıfı hiç kullanmayın!

Ayrıca adında bir özellik ekledim imageToTitleSpacing başlık ve resim arasındaki boşluğu ayarlamanıza izin veren .

Bu sınıf, kullanmak istediğiniz takdirde uyumlu olacak şekilde onun geleni imageEdgeInsets, titleEdgeInsetsve contentEdgeInsetsyeni düzen özellikleriyle doğrudan veya combinaison içinde.

@Ravron'un açıkladığı gibi, düğme içeriğinin kenarını doğru yapmak için elimden geleni yapıyorum (kırmızı kenarlıklarla görebileceğiniz gibi).

Arayüz Oluşturucu'da da kullanabilirsiniz:

  1. UIButton oluştur
  2. Düğme sınıfını değiştirme
  3. Düzenlenebilir Nitelikleri "orta", "üst", "alt", "sol" veya "sağ" kullanarak ayarlayın düğme özellikleri

İşte kod ( gist ):

@IBDesignable
class LayoutableButton: UIButton {

    enum VerticalAlignment : String {
        case center, top, bottom, unset
    }


    enum HorizontalAlignment : String {
        case center, left, right, unset
    }


    @IBInspectable
    var imageToTitleSpacing: CGFloat = 8.0 {
        didSet {
            setNeedsLayout()
        }
    }


    var imageVerticalAlignment: VerticalAlignment = .unset {
        didSet {
            setNeedsLayout()
        }
    }

    var imageHorizontalAlignment: HorizontalAlignment = .unset {
        didSet {
            setNeedsLayout()
        }
    }

    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageVerticalAlignment' instead.")
    @IBInspectable
    var imageVerticalAlignmentName: String {
        get {
            return imageVerticalAlignment.rawValue
        }
        set {
            if let value = VerticalAlignment(rawValue: newValue) {
                imageVerticalAlignment = value
            } else {
                imageVerticalAlignment = .unset
            }
        }
    }

    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageHorizontalAlignment' instead.")
    @IBInspectable
    var imageHorizontalAlignmentName: String {
        get {
            return imageHorizontalAlignment.rawValue
        }
        set {
            if let value = HorizontalAlignment(rawValue: newValue) {
                imageHorizontalAlignment = value
            } else {
                imageHorizontalAlignment = .unset
            }
        }
    }

    var extraContentEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var contentEdgeInsets: UIEdgeInsets {
        get {
            return super.contentEdgeInsets
        }
        set {
            super.contentEdgeInsets = newValue
            self.extraContentEdgeInsets = newValue
        }
    }

    var extraImageEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var imageEdgeInsets: UIEdgeInsets {
        get {
            return super.imageEdgeInsets
        }
        set {
            super.imageEdgeInsets = newValue
            self.extraImageEdgeInsets = newValue
        }
    }

    var extraTitleEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var titleEdgeInsets: UIEdgeInsets {
        get {
            return super.titleEdgeInsets
        }
        set {
            super.titleEdgeInsets = newValue
            self.extraTitleEdgeInsets = newValue
        }
    }

    //Needed to avoid IB crash during autolayout
    override init(frame: CGRect) {
        super.init(frame: frame)
    }


    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.imageEdgeInsets = super.imageEdgeInsets
        self.titleEdgeInsets = super.titleEdgeInsets
        self.contentEdgeInsets = super.contentEdgeInsets
    }

    override func layoutSubviews() {
        if let imageSize = self.imageView?.image?.size,
            let font = self.titleLabel?.font,
            let textSize = self.titleLabel?.attributedText?.size() ?? self.titleLabel?.text?.size(attributes: [NSFontAttributeName: font]) {

            var _imageEdgeInsets = UIEdgeInsets.zero
            var _titleEdgeInsets = UIEdgeInsets.zero
            var _contentEdgeInsets = UIEdgeInsets.zero

            let halfImageToTitleSpacing = imageToTitleSpacing / 2.0

            switch imageVerticalAlignment {
            case .bottom:
                _imageEdgeInsets.top = (textSize.height + imageToTitleSpacing) / 2.0
                _imageEdgeInsets.bottom = (-textSize.height - imageToTitleSpacing) / 2.0
                _titleEdgeInsets.top = (-imageSize.height - imageToTitleSpacing) / 2.0
                _titleEdgeInsets.bottom = (imageSize.height + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
            case .top:
                _imageEdgeInsets.top = (-textSize.height - imageToTitleSpacing) / 2.0
                _imageEdgeInsets.bottom = (textSize.height + imageToTitleSpacing) / 2.0
                _titleEdgeInsets.top = (imageSize.height + imageToTitleSpacing) / 2.0
                _titleEdgeInsets.bottom = (-imageSize.height - imageToTitleSpacing) / 2.0
                _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
            case .center:
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
                break
            case .unset:
                break
            }

            switch imageHorizontalAlignment {
            case .left:
                _imageEdgeInsets.left = -halfImageToTitleSpacing
                _imageEdgeInsets.right = halfImageToTitleSpacing
                _titleEdgeInsets.left = halfImageToTitleSpacing
                _titleEdgeInsets.right = -halfImageToTitleSpacing
                _contentEdgeInsets.left = halfImageToTitleSpacing
                _contentEdgeInsets.right = halfImageToTitleSpacing
            case .right:
                _imageEdgeInsets.left = textSize.width + halfImageToTitleSpacing
                _imageEdgeInsets.right = -textSize.width - halfImageToTitleSpacing
                _titleEdgeInsets.left = -imageSize.width - halfImageToTitleSpacing
                _titleEdgeInsets.right = imageSize.width + halfImageToTitleSpacing
                _contentEdgeInsets.left = halfImageToTitleSpacing
                _contentEdgeInsets.right = halfImageToTitleSpacing
            case .center:
                _imageEdgeInsets.left = textSize.width / 2.0
                _imageEdgeInsets.right = -textSize.width / 2.0
                _titleEdgeInsets.left = -imageSize.width / 2.0
                _titleEdgeInsets.right = imageSize.width / 2.0
                _contentEdgeInsets.left = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
                _contentEdgeInsets.right = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
            case .unset:
                break
            }

            _contentEdgeInsets.top += extraContentEdgeInsets.top
            _contentEdgeInsets.bottom += extraContentEdgeInsets.bottom
            _contentEdgeInsets.left += extraContentEdgeInsets.left
            _contentEdgeInsets.right += extraContentEdgeInsets.right

            _imageEdgeInsets.top += extraImageEdgeInsets.top
            _imageEdgeInsets.bottom += extraImageEdgeInsets.bottom
            _imageEdgeInsets.left += extraImageEdgeInsets.left
            _imageEdgeInsets.right += extraImageEdgeInsets.right

            _titleEdgeInsets.top += extraTitleEdgeInsets.top
            _titleEdgeInsets.bottom += extraTitleEdgeInsets.bottom
            _titleEdgeInsets.left += extraTitleEdgeInsets.left
            _titleEdgeInsets.right += extraTitleEdgeInsets.right

            super.imageEdgeInsets = _imageEdgeInsets
            super.titleEdgeInsets = _titleEdgeInsets
            super.contentEdgeInsets = _contentEdgeInsets

        } else {
            super.imageEdgeInsets = extraImageEdgeInsets
            super.titleEdgeInsets = extraTitleEdgeInsets
            super.contentEdgeInsets = extraContentEdgeInsets
        }

        super.layoutSubviews()
    }
}

1
IB'yi kırmamak için bazı şeyleri düzeltirim error: IB Designables: Failed to update auto layout status: The agent crashed, gist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb
nebiros

@ nebiros neyin yanlış olduğunu ve nasıl düzelttiğinizi açıklayabilir misiniz, lütfen?
gbitaudeau

Ben kopyalayıp senaryoyu yapıştırdığınızda @gbitaudeau, o hata var, error: IB Designables: Failed to update auto layout status: The agent crashedçünkü init(frame: CGRect)geçersiz değildi, aynı zamanda, ben eklemek @availableek açıklama ... şunları yapabilirsiniz diff -Naursen ;-), isterseniz
Nebiros

9

Yerel değişikliklerin hesaplanması için Riley Avron cevabına küçük bir ekleme:

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        let writingDirection = UIApplication.sharedApplication().userInterfaceLayoutDirection
        let factor: CGFloat = writingDirection == .LeftToRight ? 1 : -1

        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount*factor, bottom: 0, right: insetAmount*factor)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount*factor, bottom: 0, right: -insetAmount*factor)
        self.contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

6

Swift 4.x

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        let writingDirection = UIApplication.shared.userInterfaceLayoutDirection
        let factor: CGFloat = writingDirection == .leftToRight ? 1 : -1

        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount*factor, bottom: 0, right: insetAmount*factor)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount*factor, bottom: 0, right: -insetAmount*factor)
        self.contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

Kullanımı :

button.centerTextAndImage(spacing: 10.0)

Custiom görüntü boyutu ile nasıl kullanılabilir?
midhun p

2

Bewlow kodunu yazıyorum. Ürün sürümünde iyi çalışır. Swift 4.2 + 'ı destekleyin

extension UIButton{
 enum ImageTitleRelativeLocation {
    case imageUpTitleDown
    case imageDownTitleUp
    case imageLeftTitleRight
    case imageRightTitleLeft
}
 func centerContentRelativeLocation(_ relativeLocation: 
                                      ImageTitleRelativeLocation,
                                   spacing: CGFloat = 0) {
    assert(contentVerticalAlignment == .center,
           "only works with contentVerticalAlignment = .center !!!")

    guard (title(for: .normal) != nil) || (attributedTitle(for: .normal) != nil) else {
        assert(false, "TITLE IS NIL! SET TITTLE FIRST!")
        return
    }

    guard let imageSize = self.currentImage?.size else {
        assert(false, "IMGAGE IS NIL! SET IMAGE FIRST!!!")
        return
    }
    guard let titleSize = titleLabel?
        .systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) else {
            assert(false, "TITLELABEL IS NIL!")
            return
    }

    let horizontalResistent: CGFloat
    // extend contenArea in case of title is shrink
    if frame.width < titleSize.width + imageSize.width {
        horizontalResistent = titleSize.width + imageSize.width - frame.width
        print("horizontalResistent", horizontalResistent)
    } else {
        horizontalResistent = 0
    }

    var adjustImageEdgeInsets: UIEdgeInsets = .zero
    var adjustTitleEdgeInsets: UIEdgeInsets = .zero
    var adjustContentEdgeInsets: UIEdgeInsets = .zero

    let verticalImageAbsOffset = abs((titleSize.height + spacing) / 2)
    let verticalTitleAbsOffset = abs((imageSize.height + spacing) / 2)

    switch relativeLocation {
    case .imageUpTitleDown:

        adjustImageEdgeInsets.top = -verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageDownTitleUp:
        adjustImageEdgeInsets.top = verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = -verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageLeftTitleRight:
        adjustImageEdgeInsets.left = -spacing / 2
        adjustImageEdgeInsets.right = spacing / 2

        adjustTitleEdgeInsets.left = spacing / 2
        adjustTitleEdgeInsets.right = -spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    case .imageRightTitleLeft:
        adjustImageEdgeInsets.left = titleSize.width + spacing / 2
        adjustImageEdgeInsets.right = -titleSize.width - spacing / 2

        adjustTitleEdgeInsets.left = -imageSize.width - spacing / 2
        adjustTitleEdgeInsets.right = imageSize.width + spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    }

    imageEdgeInsets = adjustImageEdgeInsets
    titleEdgeInsets = adjustTitleEdgeInsets
    contentEdgeInsets = adjustContentEdgeInsets

    setNeedsLayout()
}
}

1

İmageEdgeInsets'in nasıl kullanıldığına dair basit bir örnek Bu, 30x30 düğmeyi, 10 piksel daha büyük bir isabet alanı (50x50) daha büyük hale getirecektir.

    var expandHittableAreaAmt : CGFloat = 10
    var buttonWidth : CGFloat = 30
    var button = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
    button.frame = CGRectMake(0, 0, buttonWidth+expandHittableAreaAmt, buttonWidth+expandHittableAreaAmt)
    button.imageEdgeInsets = UIEdgeInsetsMake(expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt)
    button.setImage(UIImage(named: "buttonImage"), forState: .Normal)
    button.addTarget(self, action: "didTouchButton:", forControlEvents:.TouchUpInside)

0

Swift 3'te zarif bir yol ve anlamak daha iyi:

override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
    let leftMargin:CGFloat = 40
    let imgWidth:CGFloat = 24
    let imgHeight:CGFloat = 24
    return CGRect(x: leftMargin, y: (contentRect.size.height-imgHeight) * 0.5, width: imgWidth, height: imgHeight)
}

override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    let leftMargin:CGFloat = 80
    let rightMargin:CGFloat = 80
    return CGRect(x: leftMargin, y: 0, width: contentRect.size.width-leftMargin-rightMargin, height: contentRect.size.height)
}
override func backgroundRect(forBounds bounds: CGRect) -> CGRect {
    let leftMargin:CGFloat = 10
    let rightMargin:CGFloat = 10
    let topMargin:CGFloat = 10
    let bottomMargin:CGFloat = 10
    return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin)
}
override func contentRect(forBounds bounds: CGRect) -> CGRect {
    let leftMargin:CGFloat = 5
    let rightMargin:CGFloat = 5
    let topMargin:CGFloat = 5
    let bottomMargin:CGFloat = 5
    return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin)
}

-1

Çözümün hızlı 4.2 sürümü aşağıdaki gibi olacaktır:

let spacing: CGFloat = 10 // the amount of spacing to appear between image and title
self.button?.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing)
self.button?.titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.