iPhone UIButton - Bildposition

116

Ich habe einen UIButtonmit Text "Explore the App" und UIImage(>) Interface Builderdarin sieht es aus wie:

[ (>) Explore the app ]

Aber ich muss dies UIImageNACH dem Text platzieren:

[ Explore the app (>) ]

Wie kann ich das UIImagenach rechts bewegen ?

Pavel Yakimenko
quelle
Sie können auch den Interface Builder verwenden. Überprüfen Sie meine Antwort hier: stackoverflow.com/questions/2765024/…
n8tr
1
Verwenden Sie einfach diese Unterklasse mit ein paar Codezeilen: github.com/k06a/RTLButton
k06a

Antworten:

161

Meine Lösung dafür ist ganz einfach

[button sizeToFit];
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width);
Teilt
quelle
4
Das funktioniert super! Das Edge-Inset-Konzept ist schwer zu verstehen. Irgendeine Idee, warum wir sowohl den linken als auch den rechten Rand einsetzen müssen? Theoretisch würde es ausreichen, wenn ich den Titel nach links und das Bild nach rechts verschiebe. Warum muss ich links und rechts einstellen?
PokerIncome.com
3
DIE BESTE LÖSUNG, DIE ICH GEFUNDEN HABE
Bimawa
2
Diese Antwort funktioniert perfekt. Die akzeptierte Antwort ist korrekt und verweist auf die richtigen API-Dokumente. Dies ist jedoch die Lösung zum Kopieren und Einfügen, um das zu tun, was das OP angefordert hat.
Mclaughlinj
Wird etwas kompliziert, wenn Sie den Schaltflächentext ändern. Die untergeordnete Lösung hat in diesem Fall für mich besser funktioniert.
Brad Goss
Vielen Dank, dass Sie mir gezeigt haben, dass Sie auf der linken und rechten Seite die gleiche Breite anwenden müssen. Ich verstehe nicht 100%, wie es funktioniert (da es manchmal sowohl die Größe als auch den Ursprung beeinflusst), aber ich konnte ähnliche Probleme lösen mit dieser Methode.
Toby
128

Ab iOS 9 scheint es ein einfacher Weg zu sein, dies zu erreichen, indem die Semantik der Ansicht erzwungen wird.

Geben Sie hier die Bildbeschreibung ein

Oder programmgesteuert mit:

button.semanticContentAttribute = .ForceRightToLeft
Alvivi
quelle
4
So sehr mir Ihre Antwort (+1) gefallen hat, ich hasse es zu sagen, dass dies möglicherweise nicht der "richtige" Weg ist, aber es ist einer der einfachsten.
Farzadshbfn
@farzadshbfn du hast recht. Ich habe dieses Wort für "einfach" geändert, scheint konsequenter.
Alvivi
@farzadshbfn Warum sollte dies nicht der "richtige" Weg sein?
Samman Bikram Thapa
3
@SammanBikramThapa IMHO wäre der richtige Weg, den UIButton zu unterordnen und layoutSubviews und Respekt semanticContentAttributein unserer Layoutlogik zu überschreiben , anstatt sich semanticContentAttributeselbst zu ändern . (Änderung des semantischen Ansatzes, wird mit der Internationalisierung nicht gut funktionieren)
farzadshbfn
@farzadshbfn ist völlig richtig. Unabhängig davon, ob diese Antwort die meisten Punkte erzielt, ist sie nicht korrekt. Möglicherweise wird UX für die Schnittstelle von rechts nach links beschädigt.
Pavel Yakimenko
86

Stellen Sie das imageEdgeInsetund ein titleEdgeInset, um die Komponenten in Ihrem Bild zu verschieben. Sie können auch eine Schaltfläche mit den Grafiken in voller Größe erstellen und diese als Hintergrundbild für die Schaltfläche verwenden (dann titleEdgeInsetszum Verschieben des Titels verwenden).

Steven Canfield
quelle
8
Es ist viel weniger Code, die Einfügungen einfach festzulegen, als eine Unterklasse zu implementieren. Das ist der springende Punkt der Einschübe. Das Bearbeiten von Frames für Unteransichten (die Sie nicht erstellt haben) fühlt sich eher wie ein Hack an.
Kim André Sand
6
@ kimsnarf, wirklich? Es ist viel weniger Arbeit (und weniger Hack), die Einfügungen zu optimieren, wenn Sie die Größe des Bildes oder die Länge des Titels geringfügig ändern?
Kirk Woll
54

Die Antwort von Raymond W ist hier am besten. Unterklasse UIButton mit benutzerdefinierten layoutSubviews. Hier ist eine layoutSubviews-Implementierung, die für mich sehr einfach funktioniert hat:

- (void)layoutSubviews
{
    // Allow default layout, then adjust image and label positions
    [super layoutSubviews];

    UIImageView *imageView = [self imageView];
    UILabel *label = [self titleLabel];

    CGRect imageFrame = imageView.frame;
    CGRect labelFrame = label.frame;

    labelFrame.origin.x = imageFrame.origin.x;
    imageFrame.origin.x = labelFrame.origin.x + CGRectGetWidth(labelFrame);

    imageView.frame = imageFrame;
    label.frame = labelFrame;
}
Chris Miles
quelle
2
Dieser Weg ist besser für den Fall, dass Sie viele Schaltflächen verwalten müssen, aber ich muss nur eine Schaltfläche ändern :)
Pavel Yakimenko
2
Wenn das Schaltflächenbild gleich Null ist, werden die Beschriftungsergebnisse falsch platziert, wahrscheinlich weil die UIImageView nicht eingefügt ist (getestet unter iOS6.0). Sie sollten die Bearbeitung von Frames nur in Betracht ziehen, wenn imageView.image nicht Null ist.
Scakko
2
Ich würde die folgende Verbesserung dieser Antwort vorschlagen, damit beide Ansichten zentriert bleiben: 'CGFloat cumulativeWidth = CGRectGetWidth (imageFrame) + CGRectGetWidth (labelFrame) + 10; CGFloat exzessive Breite = CGRectGetWidth (self.bounds) - kumulative Breite; labelFrame.origin.x = übermäßige Breite / 2; imageFrame.origin.x = CGRectGetMaxX (labelFrame) + 10; '
i-konov
Dies bricht unter iOS 7 für mich. Irgendjemand anderes? Funktioniert gut unter iOS 8.
Rounak
1
Unterstützen Sie iOS7 überhaupt nicht und Ihr Problem wird behoben sein. Sie sollten es sowieso nicht unterstützen.
Gil Sand
30

Was ist mit Unterklassen UIButtonund Überschreiben layoutSubviews?

Dann Nachbearbeitung der Standorte von self.imageView&self.titleLabel

Raymond W.
quelle
4
Dies ist so viel einfacher als alle anderen Ratschläge (wie der obige), wie man versucht, alles mit handabgestimmten Einsätzen richtig zu platzieren.
Steven Kramer
13

Eine andere einfache Möglichkeit (das ist NICHT nur iOS 9) besteht darin, UIButton in Unterklassen zu unterteilen, um diese beiden Methoden zu überschreiben

override func titleRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.titleRectForContentRect(contentRect)
    rect.origin.x = 0
    return rect
}

override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.imageRectForContentRect(contentRect)
    rect.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(rect)
    return rect
}

contentEdgeInsets wird bereits mit super berücksichtigt.

DrAL3X
quelle
Vielen Dank! Ich mag dies viel mehr als die Antworten, die layoutSubviews () unterordnen und überschreiben :)
Joe Susnick
1
Der beste Weg, dies meiner bescheidenen Meinung nach zu
tun
9

Das Erzwingen von "von rechts nach links" für die Schaltfläche ist keine Option, wenn Ihre App sowohl "von links nach rechts" als auch "von rechts nach links" unterstützt.

Die Lösung, die für mich funktioniert hat, ist eine Unterklasse, die der Schaltfläche im Storyboard hinzugefügt werden kann und gut mit Einschränkungen funktioniert (getestet in iOS 11):

class ButtonWithImageAtEnd: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let imageView = imageView, let titleLabel = titleLabel {
            let padding: CGFloat = 15
            imageEdgeInsets = UIEdgeInsets(top: 5, left: titleLabel.frame.size.width+padding, bottom: 5, right: -titleLabel.frame.size.width-padding)
            titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageView.frame.width, bottom: 0, right: imageView.frame.width)
        }

    }

}

Wobei "Auffüllen" der Abstand zwischen dem Titel und dem Bild ist.

Luisma
quelle
Natürlich .forceRightToLeftist eine Option! Verwenden Sie einfach den entgegengesetzten Wert ( .forceLeftToRight), wenn UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft.
Manmal
5

In Swift:

override func layoutSubviews(){
    super.layoutSubviews()

    let inset: CGFloat = 5

    if var imageFrame = self.imageView?.frame,
       var labelFrame = self.titleLabel?.frame {

       let cumulativeWidth = imageFrame.width + labelFrame.width + inset
       let excessiveWidth = self.bounds.width - cumulativeWidth
       labelFrame.origin.x = excessiveWidth / 2
       imageFrame.origin.x = labelFrame.origin.x + labelFrame.width + inset

       self.imageView?.frame = imageFrame
       self.titleLabel?.frame = labelFrame  
    }
}
ChikabuZ
quelle
4

Aufbauend auf der Antwort von @split ...

Die Antwort ist fantastisch, ignoriert jedoch die Tatsache, dass die Schaltfläche möglicherweise benutzerdefinierte Bild- und Titelkanteneinfügungen enthält, die zuvor festgelegt wurden (z. B. im Storyboard).

Beispielsweise möchten Sie möglicherweise, dass das Bild oben und unten im Container etwas aufgefüllt ist, das Bild jedoch weiterhin auf die rechte Seite der Schaltfläche verschoben wird.

Ich habe das Konzept mit dieser Methode erweitert:

- (void) moveImageToRightSide {
    [self sizeToFit];

    CGFloat titleWidth = self.titleLabel.frame.size.width;
    CGFloat imageWidth = self.imageView.frame.size.width;
    CGFloat gapWidth = self.frame.size.width - titleWidth - imageWidth;
    self.titleEdgeInsets = UIEdgeInsetsMake(self.titleEdgeInsets.top,
                                            -imageWidth + self.titleEdgeInsets.left,
                                            self.titleEdgeInsets.bottom,
                                            imageWidth - self.titleEdgeInsets.right);

    self.imageEdgeInsets = UIEdgeInsetsMake(self.imageEdgeInsets.top,
                                            titleWidth + self.imageEdgeInsets.left + gapWidth,
                                            self.imageEdgeInsets.bottom,
                                            -titleWidth + self.imageEdgeInsets.right - gapWidth);
}
BFar
quelle
2
// Get the size of the text and image
CGSize buttonLabelSize = [[self.button titleForState:UIControlStateNormal] sizeWithFont:self.button.titleLabel.font];
CGSize buttonImageSize = [[self.button imageForState:UIControlStateNormal] size];

// You can do this line in the xib too:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

// Adjust Edge Insets according to the above measurement. The +2 adds a little space 
self.button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -(buttonLabelSize.width+2));
self.button.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, buttonImageSize.width+2);

Dadurch wird eine rechtsbündige Schaltfläche erstellt, wie folgt:

[           button label (>)]

Die Schaltfläche passt die Breite nicht an den jeweiligen Kontext an, sodass links neben dem Etikett Platz angezeigt wird. Sie können dies lösen, indem Sie die Rahmenbreite der Schaltfläche aus der buttonLabelSize.width und der buttonImageSize.width berechnen.

Caliss
quelle
1
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
Todd Vanderlin
quelle
0

Diese Lösung funktioniert mit iOS 7 und höher

Unterklasse UIButton

@interface UIButton (Image)

- (void)swapTextWithImage;

@end

@implementation UIButton (Image)

- (void)swapTextWithImage {
   const CGFloat kDefaultPadding = 6.0f;
   CGSize buttonSize = [self.titleLabel.text sizeWithAttributes:@{
                                                               NSFontAttributeName:self.titleLabel.font
                                                               }];

   self.titleEdgeInsets = UIEdgeInsetsMake(0, -self.imageView.frame.size.width, 0, self.imageView.frame.size.width);
   self.imageEdgeInsets = UIEdgeInsetsMake(0, buttonSize.width + kDefaultPadding, 0, -buttonSize.width); 
}

@end

Verwendung (irgendwo in Ihrer Klasse):

[self.myButton setTitle:@"Any text" forState:UIControlStateNormal];
[self.myButton swapTextWithImage];
Pavel Volobuev
quelle
0

Aufbauend auf früheren Antworten. Wenn Sie einen Abstand zwischen dem Symbol und dem Titel der Schaltfläche haben möchten, muss der Code ein wenig geändert werden, um zu verhindern, dass die Beschriftung und das Symbol über die Grenzen von Schaltflächen mit intrinsischer Größe schweben.

let margin = CGFloat(4.0)
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width)
button.contentEdgeInsets = UIEdgeInsetsMake(0, margin, 0, margin)

Die letzte Codezeile ist wichtig für die Berechnung der Inhaltsgröße für das automatische Layout.

Kie
quelle
0

Hier ist meine eigene Art, die Sache zu machen (nach ungefähr 10 Jahren)

  1. Unterklasse von UIButton (Button, da wir in der Swift-Ära leben)
  2. Fügen Sie ein Bild und eine Beschriftung in eine Stapelansicht ein.
class CustomButton: Button {

    var didLayout: Bool = false // The code must be called only once

    override func layoutSubviews() {
        super.layoutSubviews()
        if !didLayout, let imageView = imageView, let titleLabel = titleLabel {
            didLayout = true
            let stack = UIStackView(arrangedSubviews: [titleLabel, imageView])
            addSubview(stack)
            stack.edgesToSuperview() // I use TinyConstraints library. You could handle the constraints directly
            stack.axis = .horizontal
        }
    }
}
Pavel Yakimenko
quelle
0

Einzeilige Lösung in Swift:

// iOS 9 and Onwards
button.semanticContentAttribute = .forceRightToLeft
Sunil Targe
quelle
Es ist nicht gut, da es das Layout für alle RTL-Benutzer zerstört. Sie müssen für sie von links nach rechts zwingen. Versuchen Sie besser eine andere Lösung von hier.
Pavel Yakimenko