UIButton: Wie zentriere ich ein Bild und einen Text mit imageEdgeInsets und titleEdgeInsets?

159

Wenn ich nur ein Bild in eine Schaltfläche einfüge und die imageEdgeInsets näher an den oberen Rand setze, bleibt das Bild zentriert und alles funktioniert wie erwartet:

[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];

Wenn ich nur einen Text in eine Schaltfläche einfüge und titleEdgeInsets näher am unteren Rand setze, bleibt der Text zentriert und alles funktioniert wie erwartet:

[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];

Wenn ich jedoch die 4 Zeilen zusammenstelle, stört der Text das Bild und beide verlieren die Ausrichtung in der Mitte.

Alle meine Bilder haben eine Breite von 30 Pixel. Wenn ich 30 in den linken Parameter von UIEdgeInsetMake für setTitleEdgeInsets eingebe, wird der Text erneut zentriert. Das Problem ist, dass das Bild nie zentriert wird, da es anscheinend von der Größe von button.titleLabel abhängt. Ich habe bereits viele Berechnungen mit Schaltflächengröße, Bildgröße, titleLabel-Größe versucht und nie beide perfekt zentriert.

Jemand hatte schon das gleiche Problem?

Reinaldoluckman
quelle

Antworten:

412

Hier ist eine allgemeine Lösung, um das Bild zentriert über dem Text zu positionieren, ohne magische Zahlen zu verwenden. Beachten Sie, dass der folgende Code veraltet ist und Sie wahrscheinlich eine der folgenden aktualisierten Versionen verwenden sollten :

// the space between the image and text
CGFloat spacing = 6.0;

// lower the text and push it left so it appears centered 
//  below the image
CGSize imageSize = button.imageView.frame.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
  0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);

// raise the image and push it right so it appears centered
//  above the text
CGSize titleSize = button.titleLabel.frame.size;
button.imageEdgeInsets = UIEdgeInsetsMake(
  - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

Die folgende Version enthält Änderungen zur Unterstützung von iOS 7+ , die in den Kommentaren unten empfohlen wurden. Ich habe diesen Code nicht selbst getestet, daher bin ich mir nicht sicher, wie gut er funktioniert oder ob er bei Verwendung unter früheren iOS-Versionen beschädigt werden würde.

// the space between the image and text
CGFloat spacing = 6.0;

// lower the text and push it left so it appears centered 
//  below the image
CGSize imageSize = button.imageView.image.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
  0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);

// raise the image and push it right so it appears centered
//  above the text
CGSize titleSize = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: button.titleLabel.font}];
button.imageEdgeInsets = UIEdgeInsetsMake(
  - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

// increase the content height to avoid clipping
CGFloat edgeOffset = fabsf(titleSize.height - imageSize.height) / 2.0;
button.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0);

Swift 5.0 Version

extension UIButton {
  func alignVertical(spacing: CGFloat = 6.0) {
    guard let imageSize = imageView?.image?.size,
      let text = titleLabel?.text,
      let font = titleLabel?.font
    else { return }

    titleEdgeInsets = UIEdgeInsets(
      top: 0.0,
      left: -imageSize.width,
      bottom: -(imageSize.height + spacing),
      right: 0.0
    )

    let titleSize = text.size(withAttributes: [.font: font])
    imageEdgeInsets = UIEdgeInsets(
      top: -(titleSize.height + spacing),
      left: 0.0,
      bottom: 0.0, right: -titleSize.width
    )

    let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0
    contentEdgeInsets = UIEdgeInsets(
      top: edgeOffset,
      left: 0.0,
      bottom: edgeOffset,
      right: 0.0
    )
  }
}
Jesse Crossen
quelle
5
Wunderbar - danke! Ich denke, viele andere Antworten beziehen sich auf diesen "harten Weg" - das sieht viel besser aus.
Joe D'Andrea
3
Ich fand das oben genannte Wok, aber ich habe absolut kein mentales Modell dafür, wie. Hat jemand einen Link zu einer bildlichen Erklärung, wie sich die einzelnen EdgeInsets-Parameter auswirken? Und warum hätte sich die Textbreite geändert?
Robert Atkins
3
Dies funktioniert nicht, wenn das Bild auf die Schaltfläche verkleinert wird. Es scheint, dass UIButton (zumindest in iOS 7) die image.size verwendet, nicht die imageView.frame.size für die Zentrierungsberechnungen.
Dan Jackson
5
@Hemang und Dan Jackson, ich nehme Ihre Vorschläge auf, ohne sie selbst zu testen. Ich finde es irgendwie lächerlich, dass ich dies ursprünglich für iOS 4 geschrieben habe, und nach so vielen Versionen müssen wir den Layout-Algorithmus von Apple noch im Wesentlichen zurückentwickeln, um eine so offensichtliche Funktion zu erhalten. Zumindest nehme ich an, dass es aus dem konsistenten Strom von Upvotes und den ebenso hackigen Antworten unten noch keine bessere Lösung gibt (keine Beleidigung beabsichtigt).
Jesse Crossen
5
In iOS8 habe ich es besser gefunden, es zu verwenden, button.currentTitleals button.titleLabel.textinsbesondere, wenn sich der Schaltflächentext jemals ändern wird. currentTitlewird sofort gefüllt, wohingegen titleLabel.textsich nur langsam ändern kann, was zu falsch ausgerichteten Einschüben führen kann.
Mjangda
59

Gefunden wie.

Konfigurieren Sie zunächst den Text von titleLabel(aufgrund von Stilen, dh fett, kursiv usw.). Verwenden Sie dann unter setTitleEdgeInsetsBerücksichtigung der Breite Ihres Bildes:

[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
[button.titleLabel setFont:[UIFont boldSystemFontOfSize:10.0]];

// Left inset is the negative of image width.
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, -image.size.width, -25.0, 0.0)]; 

Verwenden Sie danach unter setTitleEdgeInsetsBerücksichtigung der Breite der Textgrenzen:

[button setImage:image forState:UIControlStateNormal];

// Right inset is the negative of text bounds width.
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, -button.titleLabel.bounds.size.width)];

Jetzt werden das Bild und der Text zentriert (in diesem Beispiel wird das Bild über dem Text angezeigt).

Prost.

Reinaldoluckman
quelle
3
7 Jahre, Mann. Wirklich nicht erinnern. Als ich fragte, antwortete ich mir selbst (meine Antwort war die einzige zu der Zeit). Ich habe die ausgewählte Antwort geändert, als sich der Autor der aktuell ausgewählten Antwort darauf konzentrierte, diese magischen Zahlen zu eliminieren. In der ausgewählten Antwort können Sie also herausfinden, was sie bedeuten.
Reinaldoluckman
20

Sie können dies mit dieser Swift-Erweiterung tun, die teilweise auf Jesse Crossens Antwort basierte:

extension UIButton {
  func centerLabelVerticallyWithPadding(spacing:CGFloat) {
    // update positioning of image and title
    let imageSize = self.imageView.frame.size
    self.titleEdgeInsets = UIEdgeInsets(top:0,
                                        left:-imageSize.width,
                                        bottom:-(imageSize.height + spacing),
                                        right:0)
    let titleSize = self.titleLabel.frame.size
    self.imageEdgeInsets = UIEdgeInsets(top:-(titleSize.height + spacing),
                                        left:0,
                                        bottom: 0,
                                        right:-titleSize.width)

    // reset contentInset, so intrinsicContentSize() is still accurate
    let trueContentSize = CGRectUnion(self.titleLabel.frame, self.imageView.frame).size
    let oldContentSize = self.intrinsicContentSize()
    let heightDelta = trueContentSize.height - oldContentSize.height
    let widthDelta = trueContentSize.width - oldContentSize.width
    self.contentEdgeInsets = UIEdgeInsets(top:heightDelta/2.0,
                                          left:widthDelta/2.0,
                                          bottom:heightDelta/2.0,
                                          right:widthDelta/2.0)
  }
}

Dies definiert eine Funktion centerLabelVerticallyWithPadding , die die Titel- und Bildeinfügungen entsprechend .

Außerdem werden die contentEdgeInsets festgelegt, die meiner Meinung nach erforderlich sind, um dies sicherzustellen intrinsicContentSize weiterhin ordnungsgemäß funktionieren und das automatische Layout verwenden müssen.

Ich glaube, dass alle Lösungen, die UIButton in Unterklassen enthalten, technisch unzulässig sind, da Sie UIKit-Steuerelemente nicht in Unterklassen unterteilen sollen. Das heißt, theoretisch könnten sie in zukünftigen Versionen brechen.

Algen
quelle
Testen unter iOS9. Das Bild erscheint zentriert, aber der Text erscheint links :(
endavid
13

Bearbeiten: Aktualisiert für Swift 3

Wenn Sie nach einer Swift-Lösung für Jesse Crossens Antwort suchen, können Sie diese zu einer Unterklasse von UIButton hinzufügen:

override func layoutSubviews() {

    let spacing: CGFloat = 6.0

    // lower the text and push it left so it appears centered
    //  below the image
    var titleEdgeInsets = UIEdgeInsets.zero
    if let image = self.imageView?.image {
        titleEdgeInsets.left = -image.size.width
        titleEdgeInsets.bottom = -(image.size.height + spacing)
    }
    self.titleEdgeInsets = titleEdgeInsets

    // raise the image and push it right so it appears centered
    //  above the text
    var imageEdgeInsets = UIEdgeInsets.zero
    if let text = self.titleLabel?.text, let font = self.titleLabel?.font {
        let attributes = [NSFontAttributeName: font]
        let titleSize = text.size(attributes: attributes)
        imageEdgeInsets.top = -(titleSize.height + spacing)
        imageEdgeInsets.right = -titleSize.width
    }
    self.imageEdgeInsets = imageEdgeInsets

    super.layoutSubviews()
}
Thomas Verbeek
quelle
9

Es gibt hier einige großartige Beispiele, aber ich konnte dies nicht in allen Fällen zum Laufen bringen, wenn ich mich auch mit mehreren Textzeilen befasste (Textumbruch). Um es endlich zum Laufen zu bringen, habe ich einige der Techniken kombiniert:

  1. Ich habe oben das Beispiel von Jesse Crossen verwendet. Ich habe jedoch ein Problem mit der Texthöhe behoben und die Möglichkeit hinzugefügt, einen horizontalen Textrand anzugeben. Der Rand ist nützlich, wenn Text umbrochen werden darf, damit er nicht den Rand der Schaltfläche berührt:

    // the space between the image and text
    CGFloat spacing = 10.0;
    float   textMargin = 6;
    
    // get the size of the elements here for readability
    CGSize  imageSize   = picImage.size;
    CGSize  titleSize   = button.titleLabel.frame.size;
    CGFloat totalHeight = (imageSize.height + titleSize.height + spacing);      // get the height they will take up as a unit
    
    // lower the text and push it left to center it
    button.titleEdgeInsets = UIEdgeInsetsMake( 0.0, -imageSize.width +textMargin, - (totalHeight - titleSize.height), +textMargin );   // top, left, bottom, right
    
    // the text width might have changed (in case it was shortened before due to 
    // lack of space and isn't anymore now), so we get the frame size again
    titleSize = button.titleLabel.bounds.size;
    
    button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, -titleSize.width );     // top, left, bottom, right        
  2. Stellen Sie sicher, dass Sie die Textbeschriftung zum Umbrechen einrichten

    button.titleLabel.numberOfLines = 2; 
    button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
    button.titleLabel.textAlignment = UITextAlignmentCenter;
  3. Dies wird jetzt meistens funktionieren. Ich hatte jedoch einige Schaltflächen, die das Bild nicht richtig wiedergaben. Das Bild wurde entweder nach rechts oder links verschoben (es war nicht zentriert). Daher habe ich eine UIButton-Layout-Überschreibungstechnik verwendet, um die Zentrierung der imageView zu erzwingen.

    @interface CategoryButton : UIButton
    @end
    
    @implementation CategoryButton
    
    - (void)layoutSubviews
    {
        // Allow default layout, then center imageView
        [super layoutSubviews];
    
        UIImageView *imageView = [self imageView];
        CGRect imageFrame = imageView.frame;
        imageFrame.origin.x = (int)((self.frame.size.width - imageFrame.size.width)/ 2);
        imageView.frame = imageFrame;
    }
    @end
Tod Cunningham
quelle
Dies scheint eine gute Lösung zu sein, sollte button.titleLabel.numberOfLines jedoch nicht 0 sein, sodass es so viele Zeilen sein kann, wie es möchte?
Ben Lachman
In meinem Fall wollte ich nur bis zu zwei Zeilen. Andernfalls hätte das Bild Probleme mit der Gesamtgröße der Schaltfläche.
Tod Cunningham
9

Ich habe eine Methode für die Antwort von @ TodCunningham erstellt

 -(void) AlignTextAndImageOfButton:(UIButton *)button
 {
   CGFloat spacing = 2; // the amount of spacing to appear between image and title
   button.imageView.backgroundColor=[UIColor clearColor];
   button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
   button.titleLabel.textAlignment = UITextAlignmentCenter;
   // get the size of the elements here for readability
   CGSize imageSize = button.imageView.frame.size;
   CGSize titleSize = button.titleLabel.frame.size;

  // lower the text and push it left to center it
  button.titleEdgeInsets = UIEdgeInsetsMake(0.0, - imageSize.width, - (imageSize.height   + spacing), 0.0);

  // the text width might have changed (in case it was shortened before due to 
  // lack of space and isn't anymore now), so we get the frame size again
   titleSize = button.titleLabel.frame.size;

  // raise the image and push it right to center it
  button.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, -     titleSize.width);
 }
Bekir Onat Akin
quelle
7

Aktualisiert für Xcode 11+

Die in meiner ursprünglichen Antwort beschriebenen Einfügungen wurden in neueren Versionen von Xcode in den Größeninspektor verschoben. Ich bin nicht zu 100% sicher, wann der Wechsel stattgefunden hat, aber die Leser sollten den Größeninspektor überprüfen, wenn die eingefügten Informationen nicht im Attributinspektor gefunden werden. Unten finden Sie ein Beispiel für den neuen Einschubbildschirm (ab 11.5 oben im Inspektor für Größenattribute).

insets_moved_to_size_inspector

Ursprüngliche Antwort

An den anderen Antworten ist nichts auszusetzen, ich wollte jedoch nur darauf hinweisen, dass das gleiche Verhalten in Xcode visuell mit null Codezeilen erreicht werden kann. Diese Lösung ist nützlich, wenn Sie keinen berechneten Wert benötigen oder mit einem Storyboard / xib erstellen (andernfalls gelten andere Lösungen).

Hinweis - Ich verstehe, dass die Frage des OP Code erfordert. Ich gebe diese Antwort nur der Vollständigkeit halber und als logische Alternative für diejenigen, die Storyboards / Xibs verwenden.

Um den Abstand in Bild-, Titel- und Inhaltsansichten einer Schaltfläche mithilfe von Kanteneinfügungen zu ändern, können Sie die Schaltfläche / das Steuerelement auswählen und den Attributinspektor öffnen. Scrollen Sie nach unten zur Mitte des Inspektors und suchen Sie den Abschnitt für Kanteneinsätze.

Kanteneinsätze

Sie können auch auf die spezifischen Kanteneinfügungen für die Titel-, Bild- oder Inhaltsansicht zugreifen und diese ändern.

Menüpunkte

Tommie C.
quelle
Was ich nicht verstehe, ist, warum ich für einige Werte keine negativen Zahlen in das Storyboard eingeben kann.
Daniel T.
Funktioniert das für die neuere schnelle Version? Ich kann das Edge-Attribut nirgendwo finden
Brockhampton
@brockhampton - siehe aktualisierte Antwort für neuen Standort
Tommie C.
6

Kämpfe nicht gegen das System. Wenn Ihre Layouts zu komplex werden, um mit Interface Builder + möglicherweise einfachem Konfigurationscode verwaltet zu werden, führen Sie die Layouts auf einfachere Weise manuell aus layoutSubviews- dafür ist es da! Alles andere wird Hacks sein.

Erstellen Sie eine UIButton-Unterklasse und überschreiben Sie deren layoutSubviewsMethode, um Text und Bild programmgesteuert auszurichten. Oder verwenden Sie https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h, damit Sie layoutSubviews mithilfe eines Blocks implementieren können.

Steven Kramer
quelle
6

Unterklasse UIButton

- (void)layoutSubviews {
    [super layoutSubviews];
    CGFloat spacing = 6.0;
    CGSize imageSize = self.imageView.image.size;
    CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(self.frame.size.width, self.frame.size.height - (imageSize.height + spacing))];
    self.imageView.frame = CGRectMake((self.frame.size.width - imageSize.width)/2, (self.frame.size.height - (imageSize.height+spacing+titleSize.height))/2, imageSize.width, imageSize.height);
    self.titleLabel.frame = CGRectMake((self.frame.size.width - titleSize.width)/2, CGRectGetMaxY(self.imageView.frame)+spacing, titleSize.width, titleSize.height);
}
Alex
quelle
6

Aktualisierte Antwort von Jesse Crossen für Swift 4 :

extension UIButton {
    func alignVertical(spacing: CGFloat = 6.0) {
        guard let imageSize = self.imageView?.image?.size,
            let text = self.titleLabel?.text,
            let font = self.titleLabel?.font
            else { return }
        self.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0.0)
        let labelString = NSString(string: text)
        let titleSize = labelString.size(withAttributes: [kCTFontAttributeName as NSAttributedStringKey: font])
        self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: 0.0, bottom: 0.0, right: -titleSize.width)
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0)
    }
}

Verwenden Sie diesen Weg:

override func viewDidLayoutSubviews() {
    button.alignVertical()
}
Doci
quelle
Nach vielen, vielen Stunden. Dies war das einzige, was funktionierte :)
Harry Blue
4

Mit diesem Codestück erhalten Sie so etwas Titel- und Bildausrichtung

extension UIButton {
    func alignTextUnderImage() {
        guard let imageView = imageView else {
                return
        }
        self.contentVerticalAlignment = .Top
        self.contentHorizontalAlignment = .Center
        let imageLeftOffset = (CGRectGetWidth(self.bounds) - CGRectGetWidth(imageView.bounds)) / 2//put image in center
        let titleTopOffset = CGRectGetHeight(imageView.bounds) + 5
        self.imageEdgeInsets = UIEdgeInsetsMake(0, imageLeftOffset, 0, 0)
        self.titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset, -CGRectGetWidth(imageView.bounds), 0, 0)
    }
}
Bohdan Savych
quelle
2
Ich habe eine kleine Erweiterung zum Positionieren von Bild und Text innerhalb der Schaltfläche geschrieben. Wenn Sie interessiert sind, finden Sie hier den Quellcode. github.com/sssbohdan/ButtonAlignmentExtension/blob/master/…
Bohdan Savych
4

UIButton-Erweiterung mit Swift 3+ -Syntax :

extension UIButton {
    func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
        let imageSize: CGSize = imageView!.image!.size
        titleEdgeInsets = UIEdgeInsetsMake(0.0, -imageSize.width, -(imageSize.height + padding), 0.0)
        let labelString = NSString(string: titleLabel!.text!)
        let titleSize = labelString.size(attributes: [NSFontAttributeName: titleLabel!.font])
        self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + padding), 0.0, 0.0, -titleSize.width)
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0)
    }
}

Ursprüngliche Antwort: https://stackoverflow.com/a/7199529/3659227

Einzelgänger
quelle
3

Nur eine kleine Änderung an Jesse Crossens Antwort, die es perfekt für mich gemacht hat:

anstatt:

CGSize titleSize = button.titleLabel.frame.size;

Ich habe das benutzt:

CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];
Cesar
quelle
Willkommen bei SO! Anstatt eine separate Antwort hinzuzufügen (für eine geringfügige Änderung), können Sie diese direkt an Jesse schreiben, damit er seine [akzeptierte] Antwort korrekt überprüfen und aktualisieren kann (falls erforderlich).
Hemang
3

Die Verwendung button.titleLabel.frame.size.widthfunktioniert nur, solange das Etikett kurz genug ist, um nicht abgeschnitten zu werden. Wenn der Beschriftungstext abgeschnitten wird, funktioniert die Positionierung jedoch nicht. Nehmen

CGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[button titleLabel] font]];

funktioniert bei mir auch dann, wenn der beschriftungstext abgeschnitten ist.

Dezember
quelle
Sie haben Tippfehler.
Bhimbim
2

Ich habe mir vorhandene Antworten angesehen, aber ich fand auch, dass das Einstellen des Schaltflächenrahmens ein wichtiger erster Schritt ist.

Hier ist eine Funktion, die ich benutze, um das zu erledigen:

const CGFloat kImageTopOffset   = -15;
const CGFloat kTextBottomOffset = -25;

+ (void) centerButtonImageTopAndTextBottom: (UIButton*)         button 
                                     frame: (CGRect)            buttonFrame
                                      text: (NSString*)         textString
                                 textColor: (UIColor*)          textColor
                                      font: (UIFont*)           textFont
                                     image: (UIImage*)          image
                                  forState: (UIControlState)    buttonState
{
    button.frame = buttonFrame;

    [button setTitleColor: (UIColor*)       textColor
                 forState: (UIControlState) buttonState];

    [button setTitle: (NSString*) textString
            forState: (UIControlState) buttonState ];


    [button.titleLabel setFont: (UIFont*) textFont ];

    [button setTitleEdgeInsets: UIEdgeInsetsMake( 0.0, -image.size.width, kTextBottomOffset,  0.0)]; 

    [button setImage: (UIImage*)       image 
            forState: (UIControlState) buttonState ];

    [button setImageEdgeInsets: UIEdgeInsetsMake( kImageTopOffset, 0.0, 0.0,- button.titleLabel.bounds.size.width)];
}
Bamaco
quelle
2

Oder Sie können einfach diese Kategorie verwenden:

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

}


- (void)centerVertically
{  
    const CGFloat kDefaultPadding = 6.0f;

    [self centerVerticallyWithPadding:kDefaultPadding];  
}  


@end
RaffAl
quelle
1
Dies würde mit benutzerdefinierten Schriftarten nicht funktionieren. Von iOS7 +,CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName: button.titleLabel.font}];
Hemang
1

Mein Anwendungsfall machte Einfügungen unüberschaubar:

  1. Das Hintergrundbild auf der Schaltfläche bleibt konsistent
  2. Dynamischer Text und Bild ändern sich, wenn die Zeichenfolgenlänge und die Bildgröße variieren

Das habe ich letztendlich gemacht und bin ziemlich zufrieden damit:

  • Erstellen Sie die Schaltfläche im Storyboard mit einem Hintergrundbild (runder Kreis mit Unschärfe und Farbe).

  • Deklarieren Sie eine UIImageView in meiner Klasse:

    @implementation BlahViewController {
        UIImageView *_imageView;
    }
  • Erstellen Sie eine Bildansichtsinstanz auf init:

    -(id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            _imageView = [[UIImageView alloc] initWithCoder:aDecoder];
         }
         return self;
     }
  • Fügen Sie in viewDidLoad der Schaltfläche für unsere Bildansicht eine neue Ebene hinzu und legen Sie die Textausrichtung fest:

    [self.btn addSubview:_imageView];
    [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];
  • Fügen Sie bei der Schaltflächenklickmethode mein ausgewähltes Überlagerungsbild zur Bildansicht hinzu, passen Sie es an das Bild an und zentrieren Sie es in der Schaltfläche, aber verschieben Sie es nach oben 15, damit ich den Textversatz darunter platzieren kann:

    [_imageView setImage:[UIImage imageNamed:@"blahImageBlah]];
    [_imageView sizeToFit];
    _imageView.center = CGPointMake(ceilf(self.btn.bounds.size.width / 2.0f),
             ceilf((self.btn.bounds.size.height / 2.0f) - 15));
    [self.btn setTitle:@"Some new text" forState:UIControlStateNormal];

Hinweis: Ceilf () ist wichtig, um sicherzustellen, dass es für die Bildqualität an einer Pixelgrenze liegt.

Oliver Dungey
quelle
1
Auf jeden Fall auch ein besserer Ansatz für meinen Anwendungsfall, da ich die Schaltfläche einer Stapelansicht hinzufüge.
ValeCocoa
0

Angenommen, Sie möchten, dass sowohl Text als auch Bild horizontal zentriert werden, Bild über Text: Zentrieren Sie den Text aus dem Interface Builder und fügen Sie einen oberen Einschub hinzu (um Platz für das Bild zu schaffen). (Lassen Sie den linken Einschub auf 0). Verwenden Sie den Interface Builder, um das Bild auszuwählen. Die tatsächliche Position wird anhand des Codes festgelegt. Machen Sie sich also keine Sorgen, dass die Dinge in IB nicht in Ordnung aussehen. Im Gegensatz zu anderen Antworten oben funktioniert dies tatsächlich auf allen derzeit unterstützten iOS-Versionen (5, 6 und 7).

Verwerfen Sie im Code einfach die Bildansicht der Schaltfläche (indem Sie das Bild der Schaltfläche auf Null setzen), nachdem Sie das Bild aufgenommen haben (dies zentriert auch automatisch den Text, der bei Bedarf umbrochen wird). Instanziieren Sie dann Ihre eigene ImageView mit derselben Bildgröße und demselben Bild und positionieren Sie sie in der Mitte.

Auf diese Weise können Sie das Image weiterhin aus dem Interface Builder auswählen (obwohl es in IB nicht wie im Simulator ausgerichtet wird, andere Lösungen jedoch nicht für alle unterstützten iOS-Versionen kompatibel sind).

Radu Simionescu
quelle
0

Ich hatte Mühe, dies zu erreichen, weil ich auf dem Konstruktor meiner Ansicht keine Bildgröße und Textbreite erhalten konnte. Zwei kleine Änderungen an Jesses Antwort haben bei mir funktioniert:

CGFloat spacing = 3;
self.titleEdgeInsets = UIEdgeInsetsMake(0.0, - image.size.width, - (image.size.height + spacing), 0.0);
CGSize titleSize = [name sizeWithAttributes:@{NSFontAttributeName:self.titleLabel.font}];
self.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

Die Änderung sind:

  • Verwenden von [NSString sizeWithAttributes] , um die zu erhalten;
  • Holen Sie sich die Bildgröße direkt auf die UIImagestattUIImageView
Saulobrito
quelle
0

Dies funktioniert gut für mich, für mehrere Schaltflächen mit unterschiedlicher Bildbreite und unterschiedlicher Titellänge:

Unterklasse UIButton

override func layoutSubviews() {
    super.layoutSubviews()

    if let image = imageView?.image {

        let margin = 30 - image.size.width / 2
        let titleRect = titleRectForContentRect(bounds)
        let titleOffset = (bounds.width - titleRect.width - image.size.width - margin) / 2


        contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
            imageEdgeInsets = UIEdgeInsetsMake(0, margin, 0, 0)
            titleEdgeInsets = UIEdgeInsetsMake(0, (bounds.width - titleRect.width -  image.size.width - margin) / 2, 0, 0)
    }

}
Rémy Virin
quelle
0

Funktioniert gut für die Tastengröße 80x80 Pixel.

[self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)];    
[self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 0.0, 0.0)];
BADRI
quelle
0

Ich habe einige Anpassungen vorgenommen, um das Bild in der Mitte horizontal auszurichten:

// the space between the image and text
        let spacing = CGFloat(36.0);

        // lower the text and push it left so it appears centered
        //  below the image
        let imageSize = tutorialButton.imageView!.frame.size;
        tutorialButton.titleEdgeInsets = UIEdgeInsetsMake(
            0, -CGFloat(imageSize.width), -CGFloat(imageSize.height + spacing), 0.0);

        // raise the image and push it right so it appears centered
        //  above the text
        let titleSize = tutorialButton.titleLabel!.frame.size;
        tutorialButton.imageEdgeInsets = UIEdgeInsetsMake(
            -CGFloat(titleSize.height + spacing), CGFloat((tutorialButton.frame.width - imageSize.width) / 2), 0.0, -CGFloat(titleSize.width));
Nguyễn Thanh Khiêm
quelle
0

Ist es obligatorisch, Kanteneinsätze zu verwenden? Wenn nicht, können Sie versuchen, den Bezug zur mittleren übergeordneten Ansicht zu positionieren

extension UIButton 
{
    func centerImageAndTextVerticaAlignment(spacing: CGFloat) 
    {
        var titlePoint : CGPoint = convertPoint(center, fromView:superview)
        var imageViewPoint : CGPoint = convertPoint(center, fromView:superview)
        titlePoint.y += ((titleLabel?.size.height)! + spacing)/2
        imageViewPoint.y -= ((imageView?.size.height)! + spacing)/2
        titleLabel?.center = titlePoint
        imageView?.center = imageViewPoint

    }
}
Rodrigo Birriel
quelle
Die Frage fragt ausdrücklich nach der Verwendung von imageEdgeInsets und titleEdgeInsets, so dass es wahrscheinlich ist, dass es obligatorisch ist
Tibrogargan
0

Sie müssen das Bild um die Breite des Textes nach rechts verschieben. Verschieben Sie dann den Text um die Breite des Bildes nach links.

UIEdgeInsets imageEdgeInsets = self.remoteCommandsButtonLights.imageEdgeInsets;
imageEdgeInsets.left = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:[button.titleLabel font]}].width;
imageEdgeInsets.bottom = 14.0;
button.imageEdgeInsets = imageEdgeInsets;

UIEdgeInsets titleEdgeInsets = self.remoteCommandsButtonLights.titleEdgeInsets;
titleEdgeInsets.left = -button.currentImage.size.width;
titleEdgeInsets.top = 20.0;
button.titleEdgeInsets = titleEdgeInsets;

Stellen Sie dann die oberen und unteren Einsätze ein, um die Y-Achse anzupassen. Dies könnte wahrscheinlich auch programmgesteuert erfolgen, sollte jedoch für Ihre Bildgröße konstant sein. Während sich die X-Achsen-Einfügungen basierend auf der Größe der Textbeschriftung in jeder Schaltfläche ändern müssen.

pkamb
quelle
0

Fügen Sie diesen Code in der Erweiterung Swift 4.2 hinzu

 func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0){
    guard let imageViewWidth = self.imageView?.frame.width else{return}
    guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
    self.contentHorizontalAlignment = .left
    imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imagePadding - imageViewWidth / 2, bottom: 0.0, right: 0.0)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left: (bounds.width - titleLabelWidth) / 2 - imageViewWidth, bottom: 0.0, right: 0.0)
}
func moveImageRIghtTextCenter(imagePadding: CGFloat = 30.0){
    guard let imageViewWidth = self.imageView?.frame.width else{return}
    guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
    self.contentHorizontalAlignment = .right
    imageEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right: imagePadding - imageViewWidth / 2)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right:(bounds.width - titleLabelWidth) / 2 - imageViewWidth)
}
Shahzaib Maqbool
quelle
0

Nur um meine 2 Cent hineinzuwerfen, hat das bei mir funktioniert:

extension UIButton {
  public func centerImageAndTextVertically(spacing: CGFloat) {
    layoutIfNeeded()
    let contentFrame = contentRect(forBounds: bounds)
    let imageFrame = imageRect(forContentRect: contentFrame)
    let imageLeftInset = bounds.size.width * 0.5 - imageFrame.size.width * 0.5
    let imageTopInset = -(imageFrame.size.height + spacing * 0.5)
    let titleFrame = titleRect(forContentRect: contentFrame)
    let titleLeftInset = ((bounds.size.width - titleFrame.size.width) * 0.5) - imageFrame.size.width
    let titleTopInmset = titleFrame.size.height + spacing * 0.5
    imageEdgeInsets = UIEdgeInsets(top: imageTopInset, left: imageLeftInset, bottom: 0, right: 0)
    titleEdgeInsets = UIEdgeInsets(top: titleTopInmset, left: titleLeftInset, bottom: 0, right: 0)
  }
}
Chris Wooden
quelle