Ausrichten von Text und Bild auf UIButton mit imageEdgeInsets und titleEdgeInsets

247

Ich möchte ein Symbol links von den beiden Textzeilen platzieren, sodass zwischen dem Bild und dem Textanfang etwa 2-3 Pixel Abstand liegen. Das Steuerelement selbst ist mittig horizontal ausgerichtet (über Interface Builder festgelegt).

Der Knopf würde ungefähr so ​​aussehen:

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

Ich versuche dies mit contentEdgeInset, imageEdgeInsets und titleEdgeInsets ohne Erfolg zu konfigurieren. Ich verstehe, dass ein negativer Wert die Kante erweitert, während ein positiver Wert sie verkleinert, um sie näher an die Mitte zu bringen.

Ich habe es versucht:

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

Dies zeigt es jedoch nicht richtig an. Ich habe die Werte optimiert, aber wenn ich links von -5 auf -10 gehe, scheint sich der Wert nicht in der erwarteten Weise zu verschieben. -10 scootet den Text ganz nach links, also habe ich erwartet, dass -5 ihn auf halbem Weg von der linken Seite scootet, aber das tut es nicht.

Was ist die Logik hinter Insets? Ich bin nicht mit Bildplatzierungen und verwandten Begriffen vertraut.

Ich habe diese SO-Frage als Referenz verwendet, aber etwas an meinen Werten stimmt nicht. UIButton: Wie zentriere ich ein Bild und einen Text mit imageEdgeInsets und titleEdgeInsets?

Justin Galzic
quelle

Antworten:

391

Ich stimme der Dokumentation zu imageEdgeInsetsund titleEdgeInsetssollte besser sein, aber ich habe herausgefunden, wie ich die richtige Positionierung erreichen kann, ohne auf Versuch und Irrtum zurückgreifen zu müssen.

Die allgemeine Idee ist hier bei dieser Frage , aber das war, wenn Sie sowohl Text als auch Bild zentriert haben wollten. Wir möchten nicht, dass Bild und Text einzeln zentriert werden, sondern dass Bild und Text als eine Einheit zusammen zentriert werden. Dies ist in der Tat, was UIButton bereits tut, so dass wir einfach den Abstand anpassen müssen.

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);

Ich habe dies auch in eine Kategorie für UIButton umgewandelt, damit es einfach zu bedienen ist:

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

Jetzt muss ich nur noch:

[button centerButtonAndImageWithSpacing:10];

Und ich bekomme jedes Mal, was ich brauche. Kein manuelles Durcheinander mit den Kanteneinsätzen mehr.

BEARBEITEN: Bild und Text austauschen

Als Antwort auf @Javal in Kommentaren

Mit demselben Mechanismus können wir das Bild und den Text austauschen. Verwenden Sie zum Ausführen des Austauschs einfach einen negativen Abstand, geben Sie jedoch auch die Breite des Texts und des Bildes an. Dies setzt voraus, dass Frames bekannt sind und das Layout bereits ausgeführt wurde.

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

Natürlich möchten Sie wahrscheinlich eine gute Methode dafür entwickeln und möglicherweise eine Methode der zweiten Kategorie hinzufügen. Dies wird dem Leser als Übung überlassen.

Kekoa
quelle
Wenn ich unterschiedliche Titel für normal und hervorgehoben habe, wie kann ich sie neu zentrieren, wenn der Benutzer die Schaltfläche hervorhebt und wieder aufhebt?
user102008
@ user102008 Ganz andere Titel? Oder nur verschiedene Farben? Die Positionierung sollte sich nicht ändern, wenn Sie [UIButton setTitleColor:forState:]oder sogar verwenden [UIButton setTitle:forState:].
Kekoa
5
Wie korrigierst du [button sizeToFit], damit es richtig dimensioniert wird?
RonLugge
@ RonLugge Ich bin mir nicht sicher, warum Sie sizeToFit benötigen, daher kann ich Ihre Frage nicht beantworten. Es funktioniert gut für mich ohne sizeToFit.
Kekoa
Ich benötige sizeToFit, da die Schaltflächen / der Text dynamisch sind. Daher muss ich die Größe der Schaltfläche so anpassen, dass sie zur (benutzerdefinierten) Beschriftung passt. Das Problem ist, dass der zusätzliche Speicherplatz nicht kompensiert wird. Ich habe es überschrieben und die Rahmenbreite manuell um 10
erhöht
395

Ich bin etwas spät zu dieser Party, aber ich denke, ich habe etwas Nützliches hinzuzufügen.

Kekoas Antwort ist großartig, aber wie RonLugge erwähnt, kann dies dazu führen, dass die Schaltfläche nicht mehr respektiert wird sizeToFitoder, was noch wichtiger ist, dazu führt, dass die Schaltfläche ihren Inhalt abschneidet, wenn sie eine intrinsische Größe hat. Huch!

Zunächst jedoch

Eine kurze Erklärung, wie ich glaube imageEdgeInsetsund titleEdgeInsetsarbeite:

Die Dokumente fürimageEdgeInsets haben zum Teil Folgendes zu sagen:

Verwenden Sie diese Eigenschaft, um die Größe zu ändern und das effektive Zeichnungsrechteck für das Schaltflächenbild neu zu positionieren. Sie können für jeden der vier Einschübe (oben, links, unten, rechts) einen anderen Wert angeben. Ein positiver Wert verkleinert oder fügt diese Kante ein und bewegt sie näher an die Mitte der Schaltfläche. Ein negativer Wert erweitert oder verschiebt diese Kante.

Ich glaube, dass diese Dokumentation geschrieben wurde und sich vorstellte, dass der Button keinen Titel hat, nur ein Bild. Es macht viel mehr Sinn, auf diese Weise zu denken, und verhält sich wie UIEdgeInsetsgewöhnlich. Grundsätzlich wird der Bildrahmen (oder der Titel mit titleEdgeInsets) für positive Einfügungen nach innen und für negative Einfügungen nach außen verschoben.

In Ordnung und jetzt?

Ich komme dahin! Standardmäßig legen Sie ein Bild und einen Titel fest (der Schaltflächenrand ist grün, um anzuzeigen, wo er sich befindet):

Startbild;  Kein Leerzeichen zwischen Titel und Bild.

Wenn Sie einen Abstand zwischen einem Bild und einem Titel wünschen, ohne dass einer davon beschädigt wird, müssen Sie vier verschiedene Einfügungen festlegen, jeweils zwei für das Bild und den Titel. Das liegt daran, dass Sie nicht die Größe der Rahmen dieser Elemente ändern möchten , sondern nur deren Position. Wenn Sie anfangen, so zu denken, wird die notwendige Änderung in Kekoas ausgezeichneter Kategorie klar:

@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

Aber warte , sagst du, wenn ich das mache, bekomme ich folgendes:

Der Abstand ist gut, aber das Bild und der Titel befinden sich außerhalb des Ansichtsrahmens.

Oh ja! Ich habe vergessen, die Dokumente haben mich davor gewarnt. Sie sagen teilweise:

Diese Eigenschaft wird nur zum Positionieren des Bildes während des Layouts verwendet. Die Schaltfläche verwendet diese Eigenschaft nicht, um intrinsicContentSizeund zu bestimmen sizeThatFits:.

Aber es gibt eine Eigenschaft, die helfen kann, und das ist contentEdgeInsets. Die Dokumente dazu sagen teilweise:

Die Schaltfläche verwendet diese Eigenschaft, um intrinsicContentSizeund zu bestimmen sizeThatFits:.

Das klingt gut. Lassen Sie uns die Kategorie noch einmal optimieren:

@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

Und was bekommst du?

Abstand und Rahmen sind jetzt korrekt.

Sieht für mich wie ein Gewinner aus.


Arbeiten Sie in Swift und möchten Sie überhaupt nicht nachdenken? Hier ist die endgültige Version der Erweiterung in Swift:

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)
    }
}
Ravron
quelle
25
Was für eine großartige Antwort! Ja, ein paar Jahre zu spät zur Party, aber dies löste das Problem der intrinsicContentSizeUnrichtigkeit, was in diesen Tagen des automatischen Layouts sehr wichtig ist, da die ursprüngliche Antwort akzeptiert wurde.
Acey
2
Und wenn Sie den gleichen Abstand zwischen der Außenseite der Schaltfläche und dem Bild und der Beschriftung spacingwünschen, fügen Sie zu jedem der vier Werte von self.contentEdgeInsets Folgendes hinzu:self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
Erik van der Neut
1
Tolle Antwort, schade, dass es nicht ganz gut funktioniert, wenn das Bild richtig ausgerichtet ist und die Textlänge variieren kann.
Jlpiedrahita
2
Fast perfekte Antwort! Das einzige, was fehlt, ist, dass die Einfügungen von Bild und Titel invertiert werden müssen, wenn es auf der Schnittstelle von rechts nach links ausgeführt wird.
Jeeeyul
wie man das Bildverhältnis auf 1 zu 1
einstellt
39

Im Interface Builder. Wählen Sie den UIButton -> Attributes Inspector -> Edge = Title und ändern Sie die Kanteneinfügungen

Freeman
quelle
38

Auch wenn du etwas ähnliches machen willst

Geben Sie hier die Bildbeschreibung ein

Du brauchst

1.Stellen Sie die horizontale und vertikale Ausrichtung für die Schaltfläche auf ein

Geben Sie hier die Bildbeschreibung ein

  1. Finde alle erforderlichen Werte und setze sie 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)];

Dadurch werden Titel und Bild auf der Schaltfläche angeordnet.

Bitte beachten Sie auch, dass dies bei jedem Relayout aktualisiert wird


Schnell

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)
            }
        }
    }
}
gbk
quelle
29

Sie können viel Ärger vermeiden, indem Sie dies verwenden -

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

Dadurch werden alle Ihre Inhalte automatisch nach links ausgerichtet (oder wo immer Sie möchten).

Swift 3:

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;
Nishant
quelle
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center; // Tippfehler korrigiert
Naishta
25

In Xcode 8.0 können Sie dies einfach tun, indem Sie den Größeninspektor ändern insets.

Wählen Sie den UIButton -> Attributes Inspector -> gehen Sie zum Größeninspektor und ändern Sie die Inhalts-, Bild- und Titeleinfügungen.

Geben Sie hier die Bildbeschreibung ein

Wenn Sie das Bild auf der rechten Seite ändern möchten, können Sie die semantische Eigenschaft einfach Force Right-to-leftim Attributinspektor ändern .

Geben Sie hier die Bildbeschreibung ein

Sahil
quelle
in meinem Fall bewegt sich das Schaltflächenbild mit xcode 10 nie zur rechten Seite des Textes? kannst du helfen?
Satish Mavani
Hallo Satish, dies funktioniert auch gut mit xcode 10. Ich hoffe, Sie stellen das Bild nicht als Hintergrundbild ein und Sie können die Bildinsekten auch mit dem Größeninspektor ändern.
Sahil
18

Ich bin auch etwas spät zu dieser Party, aber ich denke, ich habe etwas Nützliches hinzuzufügen: o).

Ich habe eine erstellt UIButton Unterklasse erstellt, deren Zweck darin besteht, zu wählen, wo das Bild der Schaltfläche entweder vertikal oder horizontal angeordnet ist.

Dies bedeutet, dass Sie diese Art von Schaltflächen erstellen können: verschiedene Arten von Knöpfen

Hier die Details zum Erstellen dieser Schaltflächen mit meiner Klasse:

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)

Dazu habe ich 2 Attribute hinzugefügt: imageVerticalAlignmentundimageHorizontalAlignment . Natürlich, wenn Ihre Schaltfläche nur ein Bild oder einen Titel hat ... verwenden Sie diese Klasse überhaupt nicht!

Ich habe auch ein Attribut namens hinzugefügt imageToTitleSpacing dem Sie den Abstand zwischen Titel und Bild anpassen können.

Diese Klasse versucht ihr Bestes, um kompatibel zu sein, wenn Sie sie verwenden möchten imageEdgeInsets, titleEdgeInsetsund zwar contentEdgeInsetsdirekt oder in Kombination mit den neuen Layoutattributen.

Wie @ravron uns erklärt, versuche ich mein Bestes, um den Rand des Schaltflächeninhalts korrekt zu machen (wie Sie an den roten Rändern sehen können).

Sie können es auch im Interface Builder verwenden:

  1. Erstellen Sie einen UIButton
  2. Ändern Sie die Schaltflächenklasse
  3. Passen Sie Layout-Attribute mit "Mitte", "Oben", "Unten", "Links" oder "Rechts" an. Schaltflächenattribute

Hier der Code ( Kern ):

@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()
    }
}
gbitaudeau
quelle
1
Ich repariere einige Dinge , um die IB nicht zu brechen error: IB Designables: Failed to update auto layout status: The agent crashed, gist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb
nebiros
@nebiros Kannst du bitte erklären, was falsch ist und wie du es behebst?
Gbitaudeau
@gbitaudeau, wenn ich das Skript kopiere und einfüge, habe ich diesen Fehler erhalten, error: IB Designables: Failed to update auto layout status: The agent crashedweil init(frame: CGRect)nicht überschrieben wurde, füge ich auch @availableAnmerkungen hinzu ..., können diff -NaurSie, wenn Sie wollen, ;-)
nebiros
9

Eine kleine Ergänzung zur Antwort von Riley Avron auf Änderungen des Kontogebietsschemas:

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)
    }
}
Orxelm
quelle
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)
    }
}

Verwendung :

button.centerTextAndImage(spacing: 10.0)
Hemang
quelle
Wie kann mit Custiom Bildgröße verwendet werden?
Midhun p
2

Ich schreibe Code bewlow. Es funktioniert gut in der Produktversion. Supprot Swift 4.2 +

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()
}
}
Jules
quelle
1

Hier ist ein einfaches Beispiel für die Verwendung von imageEdgeInsets. Dadurch wird eine 30x30-Schaltfläche mit einer umlaufenden Fläche von 10 Pixel (50x50) vergrößert.

    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)
Harris
quelle
0

Eine elegante Art in Swift 3 und besser zu verstehen:

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)
}
Teonicel
quelle
-1

Die schnelle Version 4.2 der Lösung wäre die folgende:

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)
Soheil Novinfard
quelle