Jak umieścić obraz po prawej stronie tekstu w UIButton?

300

Nie chcę używać widoku podrzędnego, jeśli mogę tego uniknąć. Chcę mieć UIButtonobraz tła, tekst i obraz w nim. Teraz, kiedy to robię, obraz znajduje się po lewej stronie tekstu. Obraz tła, tekst i obraz mają różne stany podświetlenia.

jasongregori
źródło
Aby dodać kolejny „hack” do rosnącej listy tutaj: możesz ustawić atrybut przypisany do przycisku do przypisanego ciągu zawierającego tytuł przycisku + spację + obraz (jako załącznik NSText). Konieczne może być zmodyfikowanie granic załącznika, aby wyrównać go tak, jak chcesz (patrz stackoverflow.com/questions/26105803/… ).
Manav

Odpowiedzi:

266

Mimo że niektóre z sugerowanych odpowiedzi są bardzo kreatywne i wyjątkowo sprytne, najprostsze rozwiązanie jest następujące:

button.semanticContentAttribute = UIApplication.shared
    .userInterfaceLayoutDirection == .rightToLeft ? .forceLeftToRight : .forceRightToLeft

Tak proste jak to. Jako bonus, obraz będzie po lewej stronie w lokalizacjach od prawej do lewej.

EDYCJA : ponieważ pytanie zostało zadane kilka razy, jest to iOS 9 + .

Benzoes
źródło
88
Nie mogę uwierzyć, że ta odpowiedź była zaakceptowana. Nikt nie lokalizuje swoich aplikacji?
Zoltán
6
@pallzoltan: odpowiada to na pytanie (tj. „Jak umieścić obraz po prawej stronie tekstu w UIButton?”). Co ma z tym wspólnego lokalizacja?
Benjamin
16
Nie ma wielu sytuacji, w których nie chcesz, aby układ był „odwracany” w językach RTL. Bezpośrednie ustawienie semanticContentAttributeto tylko hack / obejście, a nie prawdziwe rozwiązanie.
Zoltán
6
Podejrzewam, że nie wiesz, co buduje osoba zadająca pytanie, więc zawsze lepiej liczyć na elastyczność w układzie.
Zoltán
2
Lokalizacja @ Zoltán nie stanowi problemu, wystarczy odwrócić właściwość w zależności od bieżących ustawień regionalnych.
manmal
560

Najprostsze rozwiązanie:

iOS 10 i nowsze wersje, Swift:

button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

Przed iOS 10 Swift / Obj-C:

button.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.titleLabel.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.imageView.transform = CGAffineTransformMakeScale(-1.0, 1.0);
Liau Jian Jie
źródło
8
Użyłem tego do widoku tytułu paska nawigacji i była usterka. Jest w porządku, gdy jest ładowany po raz pierwszy, ale po naciśnięciu kontrolera widoku i puknięciu go tytuł jest przewracany.
funk7
@WoominJoshPark Interesujące ... Mogę tylko zgadywać, że dzieje się tak, ponieważ transformacja jest animowana wewnętrznie dla nawigacyjnych animacji pop.
Liau Jian Jie
1
Odkryłem, że jeśli powoduje to skargi na konflikty ograniczeń autoukładu w czasie wykonywania, można to naprawić, dodając to w layoutSubviews ()
Vlad
1
Jak mogę umieścić więcej miejsca między tekstem a obrazem?
rohinb
2
@rohinb @ jose920405 Spróbuj ustawić ImageEdgeInsets i ContentEdgeInsets dla wypełniania (pamiętając, że zostały one odwrócone). Na przykład button.ImageEdgeInsets = new UIEdgeInsets(0, -leftPadding, 0, leftPadding); button.ContentEdgeInsets = new UIEdgeInsets(0, 0, 0, leftPadding);. To jest w Xamarin, ale powinno być łatwo przetłumaczone na Swift / Obj-C.
Lee Richardson
268

ZAKTUALIZOWANO DLA XCODE 9 (za pomocą konstruktora interfejsów)

Jest łatwiejszy sposób z Konstruktora interfejsów .

Wybierz UIButton i wybierz tę opcję w View Utilities> Semantic :

z lewej na prawą wprowadź opis zdjęcia tutaj Otóż ​​to! Ładnie i prosto!

OPCJONALNIE - drugi krok:

Jeśli chcesz dostosować odstępy między obrazem a tytułem, możesz zmienić wstawkę obrazu tutaj:

wprowadź opis zdjęcia tutaj

Mam nadzieję, że to pomaga!

Victor Rius
źródło
2
W Xcode 9.0 beta 5 (9M202q) niestety widać tylko wynik w czasie wykonywania - w scenariuszu nadal pokazuje obraz po lewej stronie. Należy również pamiętać, że z tego powodu ustawianie poprawnych wstawek wymaga prób i błędów.
PDK
3
Nie rób tego w ten sposób - przerywa to lokalizację języków od prawej do lewej.
jsadler
169

Podklasowanie UIButton jest całkowicie niepotrzebne. Zamiast tego możesz po prostu ustawić wysoką wartość lewej wstawki dla wstawek obrazu i małą prawą wstawkę dla tytułu. Coś takiego:

button.imageEdgeInsets = UIEdgeInsetsMake(0., button.frame.size.width - (image.size.width + 15.), 0., 0.);
button.titleEdgeInsets = UIEdgeInsetsMake(0., 0., 0., image.size.width);
Ben Baron
źródło
3
Udało się, ale pamiętaj tylko, że dziś z automatycznym układem musisz zrobić to na viewDidAppear, a nie na viewDidLoad
Hola Soy Edu Feliz Navidad
91

Daję Inspire48 kredyt na ten jeden. Opierając się na jego sugestiach i patrząc na inne pytanie, wpadłem na to. Podklasę UIButton i zastąp te metody.

@implementation UIButtonSubclass

- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
    CGRect frame = [super imageRectForContentRect:contentRect];
    frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) -  self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame;
}

- (CGRect)titleRectForContentRect:(CGRect)contentRect
{
    CGRect frame = [super titleRectForContentRect:contentRect];
    frame.origin.x = CGRectGetMinX(frame) - CGRectGetWidth([self imageRectForContentRect:contentRect]);
    return frame;
}

@end
jasongregori
źródło
3
UIButton jest klastrem klas i nie należy go klasyfikować do podklasy.
Scott Berrevoets
50
To nie jest prawda, dokumentacja wyraźnie wspomina o podklasach i podaje metody, które należy zastąpić dla zachowania niestandardowego układu.
Tark
2
developer.apple.com/library/ios/documentation/uikit/reference/... buttonWithType If you subclass UIButton, this method does not return an instance of your subclass. If you want to create an instance of a specific subclass, you must alloc/init the button directly i backgroundRectForBoundspodklasy, które zapewniają niestandardowe ozdoby w tle, mogą zastąpić tę metodę i zwrócić zmodyfikowany prostokąt obwiedni, aby zapobiec rysowaniu przycisku przez dowolną niestandardową treść. Żadne z nich nie wspomina o tych konkretnych metody, ale zakładam, że nie mają nic przeciwko podklasom.
christophercotton
1
Wygląda na to, że ta formuła jest lepsza do tworzenia kopii lustrzanej ramki obrazu: frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) - self.imageEdgeInsets.right + self.imageEdgeInsets.left - frame.origin.x;działa lepiej dla UIControlContentHorizontalAlignmentCenterinnych i ...
k06a
@ GwendalRoué To, że jest krótsze, nie oznacza, że ​​jest lepsze. Jest to trudniejsza metoda, która powoduje, że przycisk ignoruje rzeczywiste wstawki i może powodować awarię w językach pisanych od prawej do lewej. Dzięki tej odpowiedzi masz pełną kontrolę nad układem
Accatyyc
76

Po prostu zaktualizuj wstawki po zmianie tytułu. Musisz skompensować wstawkę równą i przeciwną wstawką po drugiej stronie.

[thebutton setTitle:title forState:UIControlStateNormal];
thebutton.titleEdgeInsets = UIEdgeInsetsMake(0, -thebutton.imageView.frame.size.width, 0, thebutton.imageView.frame.size.width);
thebutton.imageEdgeInsets = UIEdgeInsetsMake(0, thebutton.titleLabel.frame.size.width, 0, -thebutton.titleLabel.frame.size.width);
Piotr Tomasik
źródło
3
Możesz dodać [thebutton.titleLabel sizeToFit];wcześniej. Szerokość może wynosić zero, jeśli nie uruchomiono układu. To samo dotyczy rozmiaru obrazu (wystarczy użyć UIImage.size zamiast imageView)
delrox
@delrox dobry punkt. Można użyć titleWidth = [self.titleLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, self.bounds.size.height)].width;(lub jeśli obawiasz się, że ramka przycisku nie została jeszcze ustalona, ​​użyj również CGFLOAT_MAX dla wysokości) iimageWidth = self.currentImage.size.width;
Dave Goldman
1
Działa idealnie w viewDidLayoutSubviews
Gwendal Roué
Musiałem umieścić to layoutSubviewsw mojej UITableViewCellpodklasie, ale działa dobrze. Dzięki!
RyanG,
60

Wszystkie te odpowiedzi od stycznia 2016 r. Są niepotrzebne. W Konstruktorze interfejsów ustaw View Semantic na Force Right-to-Left, lub jeśli wolisz programowo, semanticContentAttribute = .forceRightToLeftspowoduje to, że obraz pojawi się po prawej stronie tekstu.

barndog
źródło
5
Niestety nie obsługuje iOS starszych niż 9. Wciąż ładna odpowiedź, tys.
Eddie
1
Z przykrością informuję, że ustawienie tego parametru na UIButton, który jest następnie używany dla UIBarButtonItem, nie spowodowało żadnych zmian.
Amelia
Jak wspomniano @Amelia, nie zadziała, jeśli zadzwonisz UIBarButtonItem(customView: button), ale zadziała, jeśli
owiniesz
@ tt.Kilew, używając XCode 8.1, działa. Ustawiam uiButton.semanticContentAttribute = .forceRightToLeft i udostępniam let nextButton = UIBarButtonItem (customView: uiButton)
Eugene Biryukov
53

W narzędziu do tworzenia interfejsów możesz skonfigurować opcje wstawek krawędzi dla UIButton, osobno dla każdej z trzech części: treści, obrazu, tytułu

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Xcode 8:

wprowadź opis zdjęcia tutaj

Gennadiy Ryabkin
źródło
3
moim zdaniem najlepsza odpowiedź to stackoverflow.com/a/39013315/1470374 ))
Gennadiy Ryabkin
25

Aktualizacja: Swift 3

class ButtonIconRight: UIButton {
    override func imageRect(forContentRect contentRect:CGRect) -> CGRect {
        var imageFrame = super.imageRect(forContentRect: contentRect)
        imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
        return imageFrame
    }

    override func titleRect(forContentRect contentRect:CGRect) -> CGRect {
        var titleFrame = super.titleRect(forContentRect: contentRect)
        if (self.currentImage != nil) {
            titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
        }
        return titleFrame
    }
}

Oryginalna odpowiedź dla Swift 2:

Rozwiązanie, które obsługuje wszystkie wyrównania w poziomie, z przykładem implementacji Swift. W razie potrzeby po prostu przetłumacz na Objective-C.

class ButtonIconRight: UIButton {
    override func imageRectForContentRect(contentRect:CGRect) -> CGRect {
        var imageFrame = super.imageRectForContentRect(contentRect)
        imageFrame.origin.x = CGRectGetMaxX(super.titleRectForContentRect(contentRect)) - CGRectGetWidth(imageFrame)
        return imageFrame
    }

    override func titleRectForContentRect(contentRect:CGRect) -> CGRect {
        var titleFrame = super.titleRectForContentRect(contentRect)
        if (self.currentImage != nil) {
            titleFrame.origin.x = CGRectGetMinX(super.imageRectForContentRect(contentRect))
        }
        return titleFrame
    }
}

Warto również zauważyć, że radzi sobie całkiem dobrze z wypustkami obrazu i tytułu.

Inspirowany odpowiedzią jasongregori;)

Jean-Baptiste
źródło
1
To rozwiązanie działało dla mnie, jednak mój obraz potrzebował trochę miejsca wokół niego, więc dodałem następujący kod: self.contentEdgeInsets = UIEdgeInsetsMake (10.0, 10.0, 10.0, 10.0)
user1354603
1
Podoba mi się w ten sposób, ponieważ możesz dodawać @IBDesignabledo klasy i widzieć, jak jest odwracana w czasie projektowania.
James Toomey
Wolę to rozwiązanie, ponieważ działa nawet po umieszczeniu na pasku nawigacji.
El Horrible
10

Jeśli trzeba to zrobić w UIBarButtonItem , należy zastosować dodatkowe zawijanie w widoku.
To zadziała

let view = UIView()
let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
view.addSubview(button)
view.frame = button.bounds
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: view)

To nie zadziała

let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
tt.Kilew
źródło
7

Oto rozwiązanie UIButtonz zawartością wyrównaną do środka. Ten kod wyrównuje obraz do prawej i pozwala na użycie imageEdgeInsetsi titleEdgeInsetscenne pozycjonowanie.

wprowadź opis zdjęcia tutaj

Podklasuj UIButtonswoją klasę niestandardową i dodaj:

- (CGRect)imageRectForContentRect:(CGRect)contentRect {
    CGRect frame = [super imageRectForContentRect:contentRect];
    CGFloat imageWidth = frame.size.width;
    CGRect titleRect = CGRectZero;
    titleRect.size = [[self titleForState:self.state] sizeWithAttributes:@{NSFontAttributeName: self.titleLabel.font}];
    titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame;
}

- (CGRect)titleRectForContentRect:(CGRect)contentRect {
    CGFloat imageWidth = [self imageForState:self.state].size.width;
    CGRect frame = [super titleRectForContentRect:contentRect];
    frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    return frame;
}
Witalij Gozhenko
źródło
1
Możesz także dodać IBDESIGNABLE do nagłówka klasy, aby obejrzeć go w storyborad yadi.sk/i/fd6Si-BJqzCFD
Nikolay Shubenkov
6

Ponieważ rozwiązanie przekształcające nie działa w iOS 11, postanowiłem napisać nowe podejście.

Dostosowanie przycisków semanticContentAttributedaje nam obraz ładnie po prawej stronie, bez konieczności przekazywania, jeśli tekst się zmieni. Z tego powodu jest to idealne rozwiązanie. Jednak nadal potrzebuję wsparcia RTL. Fakt, że aplikacja nie może zmienić kierunku układu w tej samej sesji, łatwo rozwiązuje ten problem.

To powiedziawszy, jest całkiem proste.

extension UIButton {
    func alignImageRight() {
        if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
            semanticContentAttribute = .forceRightToLeft
        }
        else {
            semanticContentAttribute = .forceLeftToRight
        }
    }
}
cnotethegr8
źródło
6

Sposób przedłużenia

Używanie rozszerzenia do ustawienia obrazu po prawej stronie z niestandardowym przesunięciem

   extension UIButton {
    func addRightImage(image: UIImage, offset: CGFloat) {
        self.setImage(image, for: .normal)
        self.imageView?.translatesAutoresizingMaskIntoConstraints = false
        self.imageView?.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0.0).isActive = true
        self.imageView?.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -offset).isActive = true
    }
}
Musa almatri
źródło
4

Szybko-rozwiń przycisk UiButton i umieść te linie

    if let imageWidth = self.imageView?.frame.width {
        self.titleEdgeInsets = UIEdgeInsetsMake(0, -imageWidth, 0, imageWidth);
    }

    if let titleWidth = self.titleLabel?.frame.width {
        let spacing = titleWidth + 20
        self.imageEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, -spacing);
    }
Pramod
źródło
3

Opierając się na eleganckim rozwiązaniu Piotra Tomasika: jeśli chcesz mieć również trochę odstępu między etykietą przycisku a obrazem , to dodaj to do wypustek krawędzi w następujący sposób (kopiowanie tutaj mojego kodu, który działa idealnie dla mnie):

    CGFloat spacing          = 3;
    CGFloat insetAmount      = 0.5 * spacing;

    // First set overall size of the button:
    button.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount);
    [button sizeToFit];

    // Then adjust title and image insets so image is flipped to the right and there is spacing between title and image:
    button.titleEdgeInsets   = UIEdgeInsetsMake(0, -button.imageView.frame.size.width - insetAmount, 0,  button.imageView.frame.size.width  + insetAmount);
    button.imageEdgeInsets   = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width + insetAmount, 0, -button.titleLabel.frame.size.width - insetAmount);

Dziękuję Piotrowi za twoje rozwiązanie!

Erik

Erik van der Neut
źródło
@lulian: Ostatnio korzystam z rozwiązania Liau Jian Jie (tutaj przyjęta odpowiedź), które działa znakomicie i jest bardzo eleganckim rozwiązaniem.
Erik van der Neut
To też nie działa, ponieważ zmienia wyrównanie tekstu.
Iulian Onofrei
3

Zrób sam. Xcode10, swift4,

Do programowania interfejsu użytkownika

wprowadź opis zdjęcia tutaj

 lazy var buttonFilter : ButtonRightImageLeftTitle = {
    var button = ButtonRightImageLeftTitle()
    button.setTitle("Playfir", for: UIControl.State.normal)
    button.setImage(UIImage(named: "filter"), for: UIControl.State.normal)
    button.backgroundColor = UIColor.red
    button.contentHorizontalAlignment = .left
    button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
    return button
}()

Wartości wstawek krawędziowych są stosowane do prostokąta w celu zmniejszenia lub rozszerzenia obszaru reprezentowanego przez ten prostokąt. Zazwyczaj wstawki krawędzi są używane podczas układu widoku do modyfikowania ramki widoku. Wartości dodatnie powodują wstawienie (lub zmniejszenie) ramki o określoną wartość. Wartości ujemne powodują przesunięcie ramki (lub rozszerzenie) o określoną wartość.

class ButtonRightImageLeftTitle: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        guard imageView != nil else { return }

        imageEdgeInsets = UIEdgeInsets(top: 5, left: (bounds.width - 35), bottom: 5, right: 5)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: -((imageView?.bounds.width)! + 10), bottom: 0, right: 0 )

    }
}

do projektowania interfejsu StoryBoard

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Nazmul Hasan
źródło
czy można to zrobić bardziej elegancko?
Zaporozhchenko Oleksandr
2

Podzakładanie i zastępowanie układu Podglądy to prawdopodobnie najlepsza droga.

Źródło: iPhone UIButton - pozycja obrazu

FeifanZ
źródło
3
Nie ma absolutnie żadnych problemów z podklasą UIButton.
niemiły
2

Wziąłem odpowiedź @ Piotra i przekształciłem ją w rozszerzenie Swift. Upewnij się, aby ustawić obraz i tytuł przed wywołaniem tego, aby przycisk miał odpowiedni rozmiar.

extension UIButton {

/// Makes the ``imageView`` appear just to the right of the ``titleLabel``.
func alignImageRight() {
    if let titleLabel = self.titleLabel, imageView = self.imageView {
        // Force the label and image to resize.
        titleLabel.sizeToFit()
        imageView.sizeToFit()
        imageView.contentMode = .ScaleAspectFit

        // Set the insets so that the title appears to the left and the image appears to the right. 
        // Make the image appear slightly off the top/bottom edges of the button.
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: -1 * imageView.frame.size.width,
            bottom: 0, right: imageView.frame.size.width)
        self.imageEdgeInsets = UIEdgeInsets(top: 4, left: titleLabel.frame.size.width,
            bottom: 4, right: -1 * titleLabel.frame.size.width)
    }
}

}

Nick Yap
źródło
2

Szybka opcja, która robi to, co chcesz, bez zabawy z wstawkami:

class RightImageButton: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let  textSize = titleLabel?.intrinsicContentSize(),
                imageSize = imageView?.intrinsicContentSize() {
            let wholeWidth = textSize.width + K.textImageGap + imageSize.width
            titleLabel?.frame = CGRect(
                x: round(bounds.width/2 - wholeWidth/2),
                y: 0,
                width: ceil(textSize.width),
                height: bounds.height)
            imageView?.frame = CGRect(
                x: round(bounds.width/2 + wholeWidth/2 - imageSize.width),
                y: RoundRetina(bounds.height/2 - imageSize.height/2),
                width: imageSize.width,
                height: imageSize.height)
        }
    }

    struct K {
        static let textImageGap: CGFloat = 5
    }

}
Chris
źródło
1

Wymienione tutaj rozwiązania przestały działać, gdy włączyłem Auto Layout . Musiałem wymyślić własne:

Podklasa UIButton i layoutSubviewsmetoda przesłonięcia :

//
//  MIThemeButtonImageAtRight.m
//  Created by Lukasz Margielewski on 7/9/13.
//

#import "MIThemeButtonImageAtRight.h"

static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets);

@implementation MIThemeButtonImageAtRight

- (void)layoutSubviews
{
    [super layoutSubviews];

    CGRect contentFrame = CGRectByApplyingUIEdgeInsets(self.bounds, self.contentEdgeInsets);

    CGRect frameIcon = self.imageView.frame;
    CGRect frameText = self.titleLabel.frame;

    frameText.origin.x = CGRectGetMinX(contentFrame) + self.titleEdgeInsets.left;
    frameIcon.origin.x = CGRectGetMaxX(contentFrame) - CGRectGetWidth(frameIcon);

    self.imageView.frame = frameIcon;
    self.titleLabel.frame = frameText;
}

@end

static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets){

    CGRect f = frame;

    f.origin.x += insets.left;
    f.size.width -= (insets.left + insets.right);
    f.origin.y += (insets.top);
    f.size.height -= (insets.top + insets.bottom);

    return f;

}

Wynik:

wprowadź opis zdjęcia tutaj

Łukasz
źródło
1

swift 3.0 Rozwiązanie migracji podane przez jasongregori

class ButtonIconRight: UIButton {
        override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
            var imageFrame = super.imageRect(forContentRect: contentRect)
           imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
        return imageFrame
        }

        override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
            var titleFrame = super.titleRect(forContentRect: contentRect)
            if (self.currentImage != nil) {
                titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
            }
            return titleFrame
        }
Sourabh Sharma
źródło
1

Zdecydowałem, że nie będę używać standardowego widoku obrazu przycisku, ponieważ proponowane rozwiązania do przenoszenia go wydawały się nieprzyzwoite. To zapewniło mi pożądaną estetykę i intuicyjnie jest zmieniać położenie przycisku, zmieniając ograniczenia:

extension UIButton {
    func addRightIcon(image: UIImage) {
        let imageView = UIImageView(image: image)
        imageView.translatesAutoresizingMaskIntoConstraints = false

        addSubview(imageView)

        let length = CGFloat(15)
        titleEdgeInsets.right += length

        NSLayoutConstraint.activate([
            imageView.leadingAnchor.constraint(equalTo: self.titleLabel!.trailingAnchor, constant: 10),
            imageView.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
            imageView.widthAnchor.constraint(equalToConstant: length),
            imageView.heightAnchor.constraint(equalToConstant: length)
        ])
    }
}

przycisk ze strzałką w prawo

Mark Hennings
źródło
To nie reaguje na stuknięcia, tekst przygasa, ale obraz nie
Teddy K
0

Swift 3:

open override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
    var frame = super.imageRect(forContentRect: contentRect)
    let  imageWidth = frame.size.width
    var titleRect = CGRect.zero
    titleRect.size = self.title(for: self.state)!.size(attributes: [NSFontAttributeName: self.titleLabel!.font])
    titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame
}

open override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    var frame = super.titleRect(forContentRect: contentRect)
    if let imageWidth = self.image(for: self.state)?.size.width {
        frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    }
    return frame
}
Alexander Volkov
źródło
0

Co z ograniczeniami? W przeciwieństwie do semanticContentAttribute, nie zmieniają semantyki. Może coś takiego:

 button.rightAnchorconstraint(equalTo: button.rightAnchor).isActive = true

lub w celu C:

[button.imageView.rightAnchor constraintEqualToAnchor:button.rightAnchor].isActive = YES;

Ostrzeżenia: nieprzetestowane, iOS 9+

pracownia skalna
źródło
0

Aby wyrównać obraz w UIButton, wypróbuj poniższy kod

btn.contentHorizontalAlignment = .right
Dhaval H. Nena
źródło
O to nie pytał autor.
Mateusz
0

Po wypróbowaniu wielu rozwiązań z Internetu nie osiągnąłem dokładnie tego wymagania. Skończyło się więc pisaniem niestandardowego kodu narzędzia. Publikowanie, aby pomóc komuś w przyszłości. Testowane na szybkim 4.2

// This function should be called in/after viewDidAppear to let view render
    func addArrowImageToButton(button: UIButton, arrowImage:UIImage = #imageLiteral(resourceName: "my_image_name") ) {
        let btnSize:CGFloat = 32
        let imageView = UIImageView(image: arrowImage)
        let btnFrame = button.frame
        imageView.frame = CGRect(x: btnFrame.width-btnSize-8, y: btnFrame.height/2 - btnSize/2, width: btnSize, height: btnSize)
        button.addSubview(imageView)
        //Imageview on Top of View
        button.bringSubviewToFront(imageView)
    }
jeet.chanchawat
źródło
0

w przypadku tego problemu możesz utworzyć UIView wewnątrz „etykiety z widokiem UIImage” i ustawić klasę UIView jako UIControl i utworzyć IBAction tak mocno na boku

wprowadź opis zdjęcia tutaj

Sushil Vyas
źródło
0

Swift 4 i 5

Zmień kierunek obrazu UIButton (RTL i LTR)

extension UIButton {
    func changeDirection(){
       isArabic ? (self.contentHorizontalAlignment = .right) : (self.contentHorizontalAlignment = .left)
        // left-right margin 
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    }
}
Rashid Latif
źródło
Co jest Utility?
Byron Coetsee
Po prostu usuwam narzędzie, jest to klasa w moim kodzie, w której mogę sprawdzić, czy wybranym językiem jest arabski czy angielski
Rashid Latif
0

Xcode 11.4 Swift 5.2

Dla każdego, kto próbuje odzwierciedlić styl przycisku Wstecz za pomocą szewronu w ten sposób:

wprowadź opis zdjęcia tutaj

import UIKit

class NextBarButton: UIBarButtonItem {

    convenience init(target: Any, selector: Selector) {

        // Create UIButton
        let button = UIButton(frame: .zero)

        // Set Title
        button.setTitle("Next", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize: 17)

        // Configure Symbol
        let config = UIImage.SymbolConfiguration(pointSize: 19.0, weight: .semibold, scale: .large)
        let image = UIImage(systemName: "chevron.right", withConfiguration: config)
        button.setImage(image, for: .normal)

        // Add Target
        button.addTarget(target, action: selector, for: .touchUpInside)

        // Put the Image on the right hand side of the button
        // Credit to liau-jian-jie for this part
        button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
        button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
        button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

        // Customise spacing to match system Back button
        button.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -18.0, bottom: 0.0, right: 0.0)
        button.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -12.0, bottom: 0.0, right: 0.0)

        self.init(customView: button)
    }
}

Realizacja:

override func viewDidLoad() {
    super.viewDidLoad()
    let nextButton = NextBarButton(target: self, selector: #selector(nextTapped))
    navigationItem.rightBarButtonItem = nextButton
}

@objc func nextTapped() {
    // your code
}
rbaldwin
źródło
0

Skończyło się na utworzeniu niestandardowego przycisku, który pozwala ustawić obraz z Inspektora. Poniżej mój kod:

import UIKit

@IBDesignable
class CustomButton: UIButton {

    @IBInspectable var leftImage: UIImage? = nil
    @IBInspectable var gapPadding: CGFloat = 0

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }

    func setup() {

        if(leftImage != nil) {
            let imageView = UIImageView(image: leftImage)
            imageView.translatesAutoresizingMaskIntoConstraints = false

            addSubview(imageView)

            let length = CGFloat(16)
            titleEdgeInsets.left += length

            NSLayoutConstraint.activate([
                imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: gapPadding),
                imageView.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
                imageView.widthAnchor.constraint(equalToConstant: length),
                imageView.heightAnchor.constraint(equalToConstant: length)
            ])
        }
    }
}

Możesz dopasować wartość wypełnienia odstępu z poziomu Inspektora, aby dostosować odstępy między tekstem a obrazem.

PS: Użyłem fragmentu kodu z odpowiedzi @Mark Hennings

Mahendra Liya
źródło