Menyelaraskan teks dan gambar pada UIButton dengan imageEdgeInsets dan titleEdgeInsets

248

Saya ingin menempatkan ikon di kiri dua baris teks sehingga ada sekitar 2-3 piksel ruang antara gambar dan awal teks. Kontrol itu sendiri adalah Center aligned secara horizontal (diatur melalui Interface Builder)

Tombolnya akan menyerupai sesuatu seperti ini:

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

Saya mencoba mengkonfigurasi ini dengan contentEdgeInset, imageEdgeInsets, dan titleEdgeInsets tetapi tidak berhasil. Saya mengerti bahwa nilai negatif memperluas tepi sementara nilai positif menyusut untuk memindahkannya lebih dekat ke tengah.

Saya mencoba:

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

tetapi ini tidak menampilkannya dengan benar. Saya telah mengubah nilai tetapi beralih dari -5 ke -10 pada nilai inset kiri tampaknya tidak memindahkannya dengan cara yang diharapkan. -10 akan menggeser teks ke kiri jadi saya berharap -5 untuk menggesernya setengah jalan dari sisi kiri tetapi tidak.

Apa logika di balik inset? Saya tidak terbiasa dengan penempatan gambar dan terminologi terkait.

Saya menggunakan pertanyaan SO ini sebagai referensi tetapi sesuatu tentang nilai-nilai saya tidak benar. UIButton: bagaimana cara memusatkan gambar dan teks menggunakan imageEdgeInsets dan titleEdgeInsets?

Justin Galzic
sumber

Jawaban:

392

Saya menyetujui dokumentasi pada imageEdgeInsetsdantitleEdgeInsets harus lebih baik, tetapi saya menemukan cara untuk mendapatkan posisi yang benar tanpa menggunakan trial and error.

Gagasan umum ada di sini pada pertanyaan ini , tetapi itu jika Anda ingin teks dan gambar terpusat. Kami tidak ingin gambar dan teks dipusatkan secara individual, kami ingin gambar dan teks dipusatkan bersama sebagai satu kesatuan. Ini sebenarnya apa yang sudah dilakukan UIButton, jadi kita hanya perlu mengatur jaraknya.

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

Saya juga mengubah ini menjadi kategori untuk UIButton sehingga akan mudah digunakan:

UIButton + Posisi.h

@interface UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;

@end

UIButton + Posisi.m

@implementation UIButton(ImageTitleCentering)

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

@end

Jadi sekarang yang harus saya lakukan adalah:

[button centerButtonAndImageWithSpacing:10];

Dan saya mendapatkan apa yang saya butuhkan setiap saat. Tidak perlu lagi mengacaukan insets edge secara manual.

EDIT: Menukar Gambar dan Teks

Menanggapi @Javal dalam komentar

Dengan menggunakan mekanisme yang sama ini, kita dapat menukar gambar dan teks. Untuk menyelesaikan swap, cukup gunakan spasi negatif tetapi juga sertakan lebar teks dan gambar. Ini akan membutuhkan bingkai untuk diketahui dan tata letak dilakukan sudah.

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

Tentu saja Anda mungkin ingin membuat metode yang bagus untuk ini, berpotensi menambahkan metode kategori kedua, ini dibiarkan sebagai latihan untuk pembaca.

Kekoa
sumber
Jika saya memiliki judul yang berbeda untuk yang normal dan yang disorot, bagaimana saya bisa memusatkannya kembali saat pengguna menyorot dan menghapus sorotan tombol?
user102008
@ user102008 Judul yang berbeda seluruhnya? Atau hanya warna berbeda? Penentuan posisi tidak akan berubah jika Anda menggunakan [UIButton setTitleColor:forState:], atau bahkan [UIButton setTitle:forState:].
Kekoa
5
Bagaimana Anda memperbaiki [tombol sizeToFit] sehingga ukurannya benar?
RonLugge
@RonLugge Saya tidak yakin mengapa Anda perlu sizeToFit, jadi saya tidak bisa menjawab pertanyaan Anda. Ini berfungsi dengan baik untuk saya tanpa menggunakan sizeToFit.
Kekoa
Saya perlu sizeToFit karena tombol / teksnya dinamis, jadi saya perlu mengukur tombol agar pas dengan label (ditentukan pengguna). Masalahnya adalah itu tidak mengkompensasi ruang tambahan. Saya akhirnya menimpanya, dan secara manual meningkatkan lebar frame dengan 10.
RonLugge
395

Saya sedikit terlambat ke pesta ini, tapi saya pikir saya punya sesuatu yang berguna untuk ditambahkan.

Jawaban Kekoa bagus, tetapi, seperti yang disebutkan RonLugge, itu dapat membuat tombol tidak lagi menghormati sizeToFitatau, yang lebih penting, dapat menyebabkan tombol untuk klip kontennya ketika itu ukuran intrinsik. Astaga!

Pertama,

Penjelasan singkat tentang bagaimana saya percaya imageEdgeInsetsdan titleEdgeInsetsbekerja:

The docs untukimageEdgeInsets memiliki berikut untuk mengatakan, sebagian:

Gunakan properti ini untuk mengubah ukuran dan memposisikan ulang persegi panjang gambar efektif untuk gambar tombol. Anda dapat menentukan nilai yang berbeda untuk masing-masing dari empat inset (atas, kiri, bawah, kanan). Nilai positif menyusut, atau insets, tepi itu — memindahkannya lebih dekat ke tengah tombol. Nilai negatif memperluas, atau mengimbangi, tepi itu.

Saya percaya bahwa dokumentasi ini ditulis membayangkan bahwa tombol tidak memiliki judul, hanya gambar. Itu jauh lebih masuk akal memikirkan cara ini, dan berperilaku seperti UIEdgeInsetsbiasanya. Pada dasarnya, bingkai gambar (atau judul, dengan titleEdgeInsets) dipindahkan ke dalam untuk insets positif dan ke luar untuk insets negatif.

Oke, lalu bagaimana?

Saya akan ke sana! Inilah yang Anda miliki secara default, mengatur gambar dan judul (batas tombol berwarna hijau hanya untuk menunjukkan di mana itu):

Gambar awal;  tidak ada ruang antara judul dan gambar.

Saat Anda ingin mengatur jarak antara gambar dan judul, tanpa menyebabkan keduanya hancur, Anda perlu mengatur empat inset yang berbeda, dua di setiap gambar dan judul. Itu karena Anda tidak ingin mengubah ukuran bingkai elemen-elemen itu, tetapi hanya posisi mereka. Ketika Anda mulai berpikir seperti ini, perubahan yang diperlukan untuk kategori luar biasa Kekoa menjadi jelas:

@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

Tapi tunggu , Anda berkata, ketika saya melakukan itu, saya mendapatkan ini:

Spasi baik, tetapi gambar dan judul berada di luar bingkai tampilan.

Oh ya! Saya lupa, dokter memperingatkan saya tentang ini. Mereka mengatakan, sebagian:

Properti ini hanya digunakan untuk memposisikan gambar selama tata letak. Tombol tidak menggunakan properti ini untuk menentukan intrinsicContentSizedan sizeThatFits:.

Tapi ada adalah properti yang dapat bantuan, dan bahwa itu contentEdgeInsets. Dokumen untuk itu mengatakan, sebagian:

Tombol ini menggunakan properti ini untuk menentukan intrinsicContentSizedan sizeThatFits:.

Boleh juga. Jadi mari kita ubah kategori sekali lagi:

@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

Dan apa yang kamu dapat?

Spasi dan bingkai sekarang benar.

Sepertinya pemenang bagi saya.


Bekerja di Swift dan tidak ingin berpikir sama sekali? Ini versi terakhir ekstensi di 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
sumber
25
Jawaban yang bagus! Ya beberapa tahun terlambat ke pesta, tapi ini menyelesaikan masalah intrinsicContentSizeyang salah, yang sangat penting di hari-hari tata letak otomatis ini sejak jawaban awal diterima.
Acey
2
Dan jika Anda ingin jumlah yang sama dari jarak antara luar tombol dan gambar dan label, kemudian tambahkan spacingke masing-masing empat nilai-nilai self.contentEdgeInsets, seperti:self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
Erik van der neut
1
Jawaban yang bagus, sayang itu tidak bekerja dengan baik ketika gambar disejajarkan dengan benar dan panjang teks dapat bervariasi.
jlpiedrahita
2
Jawabannya hampir sempurna! Satu-satunya hal yang hilang adalah bahwa insets gambar dan judul harus dibalik ketika dijalankan pada antarmuka Kanan-ke-Kiri.
jeeeyul
cara mengatur rasio gambar ke 1 banding 1
Yestay Muratov
39

Dalam antarmuka Builder. Pilih UIButton -> Attributes Inspector -> Edge = Judul dan modifikasi insets tepi

Warga kehormatan
sumber
38

Juga jika Anda ingin membuat sesuatu yang mirip

masukkan deskripsi gambar di sini

Kamu butuh

1. Atur penyelarasan horizontal dan vertikal untuk tombol

masukkan deskripsi gambar di sini

  1. Temukan semua nilai yang dibutuhkan dan atur 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)];

Ini akan mengatur judul dan gambar Anda pada tombol.

Perhatikan juga perbarui ini pada setiap relayout


Cepat

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
sumber
29

Anda dapat menghindari banyak masalah dengan menggunakan ini -

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

Ini akan menyelaraskan semua konten Anda secara otomatis ke kiri (atau di mana pun Anda inginkan)

Swift 3:

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;
Nishant
sumber
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center; // Typo dikoreksi
Naishta
25

Di Xcode 8.0 Anda cukup melakukannya dengan mengubahinsets inspektur ukuran.

Pilih tombol UIB -> Atribut Inspektur -> buka pemeriksa ukuran dan modifikasi inset konten, gambar, dan judul.

masukkan deskripsi gambar di sini

Dan jika Anda ingin mengubah gambar di sisi kanan, Anda cukup mengubah properti semantik ke Force Right-to-leftdalam Atribut inspector.

masukkan deskripsi gambar di sini

Sahil
sumber
dalam gambar tombol kasus saya tidak pernah bergerak ke sisi kanan teks dengan xcode 10? Bisakah kamu menolong?
Satish Mavani
Hai Satish, ini juga berfungsi dengan baik dengan xcode 10. Harap Anda mengatur gambar bukan gambar latar dan Anda juga dapat mengubah serangga gambar menggunakan inspektur dalam ukuran.
Sahil
18

Saya agak terlambat ke pesta ini juga, tapi saya rasa saya punya sesuatu yang berguna untuk ditambahkan: o).

Saya membuat UIButtonsubclass yang tujuannya adalah untuk dapat memilih di mana gambar tombol adalah tata letak, baik secara vertikal maupun horizontal.

Ini berarti Anda dapat membuat tombol semacam ini: berbagai jenis tombol

Di sini detail tentang cara membuat tombol ini dengan kelas saya:

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)

Untuk melakukan itu, saya menambahkan 2 atribut: imageVerticalAlignmentdan imageHorizontalAlignment. Tentu saja, Jika tombol Anda hanya memiliki gambar atau judul ... jangan gunakan kelas ini sama sekali!

Saya juga menambahkan atribut bernama imageToTitleSpacing yang memungkinkan Anda untuk menyesuaikan ruang antara judul dan gambar.

Kelas ini mencoba yang terbaik agar kompatibel jika Anda ingin menggunakan imageEdgeInsets, titleEdgeInsetsdan contentEdgeInsetslangsung atau dikombinasikan dengan atribut tata letak baru.

Sebagai @ravron menjelaskan kepada kami, saya mencoba yang terbaik untuk membuat tepi konten tombol yang benar (seperti yang Anda lihat dengan batas merah).

Anda juga dapat menggunakannya di Interface Builder:

  1. Buat UIButton
  2. Ubah kelas tombol
  3. Sesuaikan Atribut Layoutable menggunakan "tengah", "atas", "bawah", "kiri" atau "kanan" atribut tombol

Berikut kodenya ( inti ):

@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
sumber
1
Saya memperbaiki beberapa hal, untuk tidak menghancurkan IB dengan error: IB Designables: Failed to update auto layout status: The agent crashed, gist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb
nebiros
@nebiros dapatkah Anda menjelaskan apa yang salah dan bagaimana Anda memperbaikinya, tolong?
gbitaudeau
@ gbitaudeau ketika saya menyalin dan menempelkan skrip, saya mendapatkan kesalahan itu, error: IB Designables: Failed to update auto layout status: The agent crashedkarena init(frame: CGRect)tidak ditimpa, juga, saya menambahkan @availableanotasi ..., Anda dapat diff -Naurjika Anda mau, ;-)
nebiros
9

Tambahan kecil untuk jawaban Riley Avron untuk perubahan lokal akun:

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
sumber
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)
    }
}

Penggunaan :

button.centerTextAndImage(spacing: 10.0)
Hemang
sumber
Bagaimana bisa digunakan dengan ukuran gambar custiom?
midhun p
2

Saya menulis kode bewlow. Ini berfungsi dengan baik dalam versi produk. 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
sumber
1

Berikut adalah contoh sederhana tentang cara menggunakan imageEdgeInsets. Ini akan membuat tombol 30x30 dengan area hittable 10 piksel lebih besar di sekelilingnya (50x50)

    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
sumber
0

Cara yang elegan di Swift 3 dan lebih baik untuk dipahami:

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
sumber
-1

Solusi versi 4.2 yang cepat adalah sebagai berikut:

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
sumber