UIButton: วิธีจัดรูปภาพและข้อความให้อยู่กึ่งกลางโดยใช้ imageEdgeInsets และ titleEdgeInsets


159

หากฉันใส่เฉพาะภาพในปุ่มและตั้งค่า imageEdgeInsets ให้ใกล้เคียงกับด้านบนมากขึ้นภาพนั้นจะอยู่ตรงกลางและทำงานได้ตามที่คาดไว้:

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

ถ้าฉันใส่ข้อความเพียงปุ่มเดียวและตั้งค่า titleEdgeInsets ให้ใกล้กับด้านล่างมากขึ้นข้อความจะอยู่กึ่งกลางและทำงานตามที่คาดไว้:

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

แต่ถ้าฉันใส่ทั้ง 4 บรรทัดเข้าด้วยกันข้อความจะรบกวนภาพและทั้งคู่ขาดการจัดตำแหน่งกึ่งกลาง

รูปภาพของฉันทั้งหมดมีความกว้าง 30 พิกเซลและถ้าฉันใส่ 30 ในพารามิเตอร์ด้านซ้ายของ UIEdgeInsetMake สำหรับ setTitleEdgeInsets ข้อความจะอยู่กึ่งกลางอีกครั้ง ปัญหาคือภาพไม่เคยอยู่ตรงกลางเพราะปรากฏว่ามันขึ้นอยู่กับขนาดของ button.titleLabel ฉันได้ลองคำนวณหลายอย่างด้วยขนาดปุ่มขนาดภาพขนาดหัวเรื่องและไม่เคยอยู่ตรงกลางทั้งสองอย่างสมบูรณ์

มีคนปัญหาเดียวกันอยู่แล้วใช่ไหม

คำตอบ:


412

สำหรับสิ่งที่คุ้มค่าต่อไปนี้เป็นวิธีแก้ปัญหาทั่วไปในการจัดตำแหน่งภาพที่อยู่ตรงกลางเหนือข้อความโดยไม่ต้องใช้หมายเลขเวทย์มนตร์ โปรดทราบว่ารหัสต่อไปนี้ล้าสมัยและคุณควรใช้หนึ่งในรุ่นที่ปรับปรุงด้านล่าง :

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

รุ่นต่อไปนี้มีการเปลี่ยนแปลงเพื่อรองรับiOS 7+ที่ได้รับการแนะนำในความคิดเห็นด้านล่าง ฉันยังไม่ได้ทดสอบโค้ดนี้ด้วยตัวเองดังนั้นฉันจึงไม่แน่ใจว่ามันใช้งานได้ดีแค่ไหนหรือมันจะแตกถ้าใช้ภายใต้ iOS เวอร์ชันก่อนหน้านี้

// 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

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

5
ยอดเยี่ยม - ขอบคุณ! ฉันคิดว่าคำตอบอื่น ๆ มากมายเกี่ยวกับเรื่องนี้ "วิธีที่ยาก" - นี่ดูดีกว่ามาก
Joe D'Andrea

3
ฉันพบข้างต้นจะกระทะ แต่ฉันไม่มีแบบจำลองจิตอย่างแน่นอนสำหรับวิธี ทุกคนมีลิงก์ไปยังคำอธิบายรูปภาพเกี่ยวกับสิ่งที่พารามิเตอร์ EdgeInsets แต่ละรายการมีผลต่ออะไร และทำไมความกว้างของข้อความจึงเปลี่ยน?
Robert Atkins

3
สิ่งนี้ไม่ทำงานเมื่อภาพถูกย่อให้พอดีกับปุ่ม ปรากฏว่า UIButton (ใน iOS 7 เป็นอย่างน้อย) ใช้ image.size ไม่ใช่ imageView.frame.size สำหรับการคำนวณที่กึ่งกลาง
Dan Jackson

5
@Hemang และ Dan Jackson ฉันกำลังรวมข้อเสนอแนะของคุณโดยไม่ต้องทดสอบด้วยตนเอง ฉันพบว่ามันไร้สาระที่ตอนแรกฉันเขียนสิ่งนี้สำหรับ iOS 4 และหลังจากหลาย ๆ รุ่นนี้เรายังคงต้องใช้อัลกอรึทึมโครงร่างของ Apple เพื่อให้ได้คุณสมบัติที่ชัดเจน หรืออย่างน้อยฉันก็คิดว่ามันยังไม่มีทางออกที่ดีกว่าจากกระแส upvotes ที่สอดคล้องกันและคำตอบที่ถูกแฮ็กอย่างเท่าเทียมกันด้านล่าง (ไม่มีเจตนาดูถูก)
Jesse Crossen

5
ใน iOS8 ฉันพบว่ามันจะดีกว่าที่จะใช้button.currentTitleแทนbutton.titleLabel.textโดยเฉพาะถ้าข้อความปุ่มจะเปลี่ยนแปลง currentTitleมีประชากรทันทีในขณะที่titleLabel.textสามารถเปลี่ยนแปลงได้ช้าซึ่งอาจนำไปสู่การแทรกซึมไม่ตรงแนว
mjangda

59

พบได้อย่างไร

ก่อนอื่นให้กำหนดค่าข้อความของtitleLabel(เนื่องจากลักษณะเช่นตัวหนาตัวเอียง ฯลฯ ) จากนั้นใช้setTitleEdgeInsetsพิจารณาความกว้างของภาพของคุณ:

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

หลังจากนั้นให้ใช้setTitleEdgeInsetsพิจารณาความกว้างของขอบเขตข้อความ:

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

ตอนนี้รูปภาพและข้อความจะอยู่กึ่งกลาง (ในตัวอย่างนี้รูปภาพจะปรากฏเหนือข้อความ)

ไชโย


3
7 ปีที่แล้ว จำไม่ได้จริงๆ เมื่อฉันถามฉันตอบตัวเอง (คำตอบของฉันคือคนเดียวในเวลา) ฉันได้เปลี่ยนคำตอบที่เลือกไว้เมื่อผู้สร้างคำตอบที่เลือกในปัจจุบันมุ่งเน้นไปที่การกำจัดตัวเลขเวทย์ ดังนั้นในคำตอบที่เลือกคุณสามารถเข้าใจความหมายของมัน
reinaldoluckman

20

คุณสามารถทำได้ด้วยส่วนขยาย Swift นี้ซึ่งมีพื้นฐานจากคำตอบของ Jesse Crossen:

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

สิ่งนี้กำหนดฟังก์ชั่นcenterLabelVerticallyWithPaddingที่ตั้งชื่อเรื่องและรูปภาพแทรกอย่างเหมาะสม

นอกจากนี้ยังตั้งค่า contentEdgeInsets ซึ่งฉันเชื่อว่าเป็นสิ่งจำเป็นเพื่อให้แน่ใจว่าintrinsicContentSizeยังคงทำงานได้อย่างถูกต้องซึ่งจะต้องใช้เค้าโครงอัตโนมัติ

ฉันเชื่อว่าโซลูชันทั้งหมดที่ UIButton ของคลาสย่อยไม่ถูกกฎหมายในทางเทคนิคเนื่องจากคุณไม่ควรควบคุม UIKit ของคลาสย่อย เช่นในทางทฤษฎีพวกเขาอาจแตกหักในรุ่นอนาคต


ทดสอบบน iOS9 ภาพปรากฏกึ่งกลาง แต่ข้อความปรากฏทางซ้าย :(
endavid

13

แก้ไข: อัปเดตสำหรับ Swift 3

ในกรณีที่คุณกำลังมองหาคำตอบอย่างรวดเร็วของคำตอบของ Jesse Crossen คุณสามารถเพิ่มสิ่งนี้ลงในคลาสย่อยของ UIButton:

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

9

มีตัวอย่างที่ดีอยู่ที่นี่ แต่ฉันไม่สามารถทำให้มันทำงานได้ในทุกกรณีเมื่อต้องจัดการกับข้อความหลายบรรทัด (การตัดข้อความ) ในที่สุดเพื่อให้มันทำงานฉันรวมสองสามเทคนิค:

  1. ฉันใช้ตัวอย่าง Jesse Crossen ด้านบน อย่างไรก็ตามฉันแก้ไขปัญหาความสูงของข้อความและฉันเพิ่มความสามารถในการระบุระยะขอบข้อความในแนวนอน ระยะขอบมีประโยชน์เมื่ออนุญาตให้ตัดข้อความดังนั้นจึงไม่กดปุ่มที่ขอบ:

    // 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. ตรวจสอบให้แน่ใจว่าคุณได้ติดตั้งป้ายกำกับข้อความเพื่อตัดคำ

    button.titleLabel.numberOfLines = 2; 
    button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
    button.titleLabel.textAlignment = UITextAlignmentCenter;
  3. ส่วนใหญ่จะใช้งานได้ในขณะนี้ อย่างไรก็ตามฉันมีปุ่มบางปุ่มที่ไม่สามารถแสดงภาพได้อย่างถูกต้อง รูปภาพถูกเลื่อนไปทางขวาหรือซ้าย (ไม่ได้อยู่กึ่งกลาง) ดังนั้นฉันจึงใช้เทคนิคการแทนที่เค้าโครง UIButton เพื่อบังคับให้ imageView ให้อยู่กึ่งกลาง

    @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

ดูเหมือนว่านี่เป็นวิธีการแก้ปัญหาที่ดี แต่ไม่ควร button.titleLabel.numberOfLines เป็น 0 อย่างที่มันสามารถมีได้หลายบรรทัดตามที่ต้องการ?
Ben Lachman

ในกรณีของฉันฉันต้องการแค่สองบรรทัดเท่านั้น มิฉะนั้นภาพจะมีปัญหา wrt ขนาดโดยรวมของปุ่ม
Tod Cunningham

9

ฉันสร้างวิธีการสำหรับคำตอบของ @ TodCunningham

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

7

อัปเดตสำหรับ Xcode 11+

สิ่งที่อธิบายไว้ในคำตอบเดิมของฉันถูกย้ายไปยังผู้ตรวจสอบขนาดใน Xcode เวอร์ชันล่าสุด ฉันไม่ชัดเจน 100% เมื่อสวิตช์เกิดขึ้น แต่ผู้อ่านควรตรวจสอบขนาดตัวตรวจสอบหากไม่พบข้อมูลสิ่งที่ใส่เข้าไปในตัวตรวจสอบคุณลักษณะ ด้านล่างเป็นตัวอย่างของหน้าจอสิ่งที่ใส่เข้าไปใหม่ (อยู่ที่ด้านบนของขนาดผู้ตรวจสอบคุณลักษณะขนาดตั้งแต่ 11.5)

insets_moved_to_size_inspector

คำตอบเดิม

ไม่มีอะไรผิดปกติกับคำตอบอื่น ๆ แต่ฉันแค่อยากจะทราบว่าพฤติกรรมเดียวกันนี้สามารถทำได้โดยใช้ Xcode โดยใช้โค้ดศูนย์ โซลูชันนี้มีประโยชน์หากคุณไม่ต้องการค่าที่คำนวณได้หรือกำลังสร้างด้วยสตอรีบอร์ด / xib (ไม่เช่นนั้นจะใช้วิธีแก้ไขปัญหาอื่น)

หมายเหตุ - ฉันเข้าใจว่าคำถามของ OP คือรหัสที่ต้องใช้ ฉันแค่ให้คำตอบนี้เพื่อความสมบูรณ์และเป็นทางเลือกเชิงตรรกะสำหรับผู้ใช้กระดานเรื่องราว / xib

ในการปรับเปลี่ยนระยะห่างของภาพชื่อเรื่องและมุมมองเนื้อหาของปุ่มโดยใช้การแทรกขอบคุณสามารถเลือกปุ่ม / การควบคุมและเปิดตัวตรวจสอบคุณสมบัติ เลื่อนลงไปทางกลางของผู้ตรวจสอบและค้นหาส่วนสำหรับการแทรกขอบ

ขอบขุ่น

ท่านสามารถเข้าถึงและแก้ไขส่วนเสริมของขอบสำหรับมุมมองหัวเรื่องภาพหรือเนื้อหา

เมนูตัวเลือก


สิ่งที่ฉันไม่เข้าใจคือสาเหตุที่ฉันไม่สามารถป้อนจำนวนลบในกระดานเรื่องราวสำหรับค่าบางอย่าง
Daniel T.

มันใช้งานได้กับเวอร์ชั่นที่รวดเร็วกว่าใหม่หรือไม่? ฉันหาแอตทริบิวต์ Edge ไม่ได้ทุกที่
brockhampton

@brockhampton - ดูคำตอบที่ปรับปรุงแล้วสำหรับตำแหน่งใหม่
Tommie C.

6

อย่าต่อสู้กับระบบ หากเลย์เอาต์ของคุณซับซ้อนเกินกว่าที่จะจัดการโดยใช้ Interface Builder + บางทีรหัสการกำหนดค่าง่าย ๆ ให้ทำเลย์เอาต์ด้วยตนเองในวิธีที่ง่ายกว่าโดยใช้layoutSubviewsนั่นคือสิ่งที่มันต้องการ! ทุกอย่างอื่นจะเป็นจำนวนมากที่จะแฮ็ก

สร้างคลาสย่อย UIButton และแทนที่layoutSubviewsเมธอดเพื่อจัดแนวข้อความและรูปภาพของคุณโดยทางโปรแกรม หรือใช้บางอย่างเช่นhttps://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.hเพื่อให้คุณสามารถใช้ layoutSubviews โดยใช้บล็อก


6

คลาสย่อย 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);
}

6

คำตอบล่าสุดของJesse Crossenสำหรับ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)
    }
}

ใช้วิธีนี้:

override func viewDidLayoutSubviews() {
    button.alignVertical()
}

หลังจากหลายชั่วโมง นี่เป็นเพียงสิ่งเดียวที่ใช้ได้ผล :)
Harry Blue

4

ด้วยโค้ดอันนี้คุณจะได้รับสิ่งนี้ การจัดตำแหน่งชื่อและรูปภาพ

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

2
ฉันเขียนส่วนขยายเล็ก ๆ สำหรับวางรูปภาพและข้อความไว้ในปุ่ม หากคุณสนใจนี่คือรหัสที่มา github.com/sssbohdan/ButtonAlignmentExtension/blob/master/…
Bohdan Savych

4

ส่วนขยาย UIButton พร้อมSwift 3+ไวยากรณ์:

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

คำตอบเดิม: https://stackoverflow.com/a/7199529/3659227


3

เพียงแค่การเปลี่ยนแปลงเล็กน้อยกับคำตอบของ Jesse Crossen ที่ทำให้มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน:

แทน:

CGSize titleSize = button.titleLabel.frame.size;

ฉันเคยใช้สิ่งนี้:

CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];

ยินดีต้อนรับสู่ SO! แทนที่จะเพิ่มคำตอบแยกต่างหาก (สำหรับการเปลี่ยนแปลงเล็กน้อย) คุณสามารถเขียนสิ่งนี้กับ Jesse โดยตรงเพื่อให้เขาสามารถตรวจสอบและอัปเดตคำตอบ [ยอมรับ] อย่างถูกต้อง (ถ้าจำเป็น)
Hemang

3

ใช้button.titleLabel.frame.size.widthงานได้ดีตราบใดที่ฉลากสั้นพอที่จะไม่ถูกตัดทอน เมื่อข้อความฉลากถูกตัดทอนตำแหน่งจะไม่ทำงาน สละ

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

ใช้ได้กับฉันแม้ว่าข้อความฉลากจะถูกตัดทอน


คุณพิมพ์ผิด
Bhimbim

2

ฉันดูคำตอบที่มีอยู่แล้ว แต่ฉันก็พบว่าการตั้งค่ากรอบปุ่มเป็นขั้นตอนแรกที่สำคัญ

นี่คือฟังก์ชั่นที่ฉันใช้ที่ดูแลเรื่องนี้:

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

2

หรือคุณสามารถใช้หมวดหมู่นี้:

@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

1
สิ่งนี้จะไม่ทำงานกับแบบอักษรที่กำหนดเอง ตั้งแต่ iOS7 +,CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName: button.titleLabel.font}];
Hemang

1

กรณีการใช้งานของฉันทำให้สิ่งที่ใส่เข้าไปไม่สามารถจัดการได้:

  1. ภาพพื้นหลังที่ปุ่มยังคงที่
  2. การเปลี่ยนแปลงข้อความและรูปภาพแบบไดนามิกโดยที่ความยาวและขนาดสตริงแตกต่างกันไป

นี่คือสิ่งที่ฉันทำลงไปและฉันก็มีความสุขกับมัน:

  • สร้างปุ่มบนกระดานเรื่องราวด้วยภาพพื้นหลัง (วงกลมรอบที่มีความพร่ามัวและสี)

  • ประกาศ UIImageView ในชั้นเรียนของฉัน:

    @implementation BlahViewController {
        UIImageView *_imageView;
    }
  • สร้างอินสแตนซ์ดูภาพใน init:

    -(id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            _imageView = [[UIImageView alloc] initWithCoder:aDecoder];
         }
         return self;
     }
  • ใน viewDidLoad เพิ่มเลเยอร์ใหม่ให้กับปุ่มสำหรับมุมมองภาพของเราและตั้งค่าการจัดแนวข้อความ:

    [self.btn addSubview:_imageView];
    [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];
  • ในวิธีการคลิกปุ่มเพิ่มภาพซ้อนทับที่ฉันเลือกไว้ในมุมมองภาพปรับขนาดให้พอดีกับภาพและจัดให้อยู่กึ่งกลางปุ่ม แต่เลื่อนขึ้นเป็น 15 เพื่อให้ฉันสามารถวางข้อความตรงข้ามด้านล่าง:

    [_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];

หมายเหตุ: ceilf () เป็นสิ่งสำคัญเพื่อให้แน่ใจว่าอยู่ในขอบเขตพิกเซลสำหรับคุณภาพของภาพ


1
วิธีที่ดีกว่าสำหรับกรณีการใช้งานของฉันเช่นกันเนื่องจากฉันเพิ่มปุ่มลงในมุมมองสแต็ก
valeCocoa

0

สมมติว่าคุณต้องการให้ข้อความและภาพอยู่กึ่งกลางในแนวนอนภาพด้านบนข้อความ: จัดกึ่งกลางข้อความจากตัวสร้างส่วนต่อประสานและเพิ่มส่วนบนสุด (เพิ่มที่ว่างสำหรับภาพ) (ปล่อยสิ่งที่ใส่เข้าไปทางซ้ายเป็น 0) ใช้ตัวสร้างส่วนต่อประสานเพื่อเลือกภาพ - ตำแหน่งจริงจะถูกตั้งค่าจากรหัสดังนั้นไม่ต้องกังวลว่าสิ่งต่าง ๆ จะดูไม่ดีใน IB ต่างจากคำตอบอื่น ๆ ข้างต้นนี่ใช้งานได้กับ ios ทุกรุ่นที่รองรับในปัจจุบัน (5,6 และ 7)

ในรหัสเพียงแค่ทิ้ง ImageView ของปุ่ม (โดยการตั้งค่าภาพของปุ่มเป็นโมฆะ) หลังจากที่จับภาพ (สิ่งนี้จะจัดกึ่งกลางข้อความโดยอัตโนมัติถ้าจำเป็น) จากนั้นยกตัวอย่าง ImageView ของคุณเองด้วยขนาดเฟรมและรูปภาพที่เหมือนกันแล้วจัดวางไว้ตรงกลาง

วิธีนี้คุณยังคงสามารถเลือกรูปภาพจากเครื่องมือสร้างส่วนต่อประสาน (แม้ว่าจะไม่ได้จัดแนวใน IB เหมือนกับในโปรแกรมจำลอง แต่หลังจากนั้นโซลูชันอื่น ๆ ก็ไม่สามารถทำงานร่วมกันได้ในทุกรุ่นของ iOS ที่รองรับ)


0

ฉันพยายามทำสิ่งนี้เพราะฉันไม่สามารถรับขนาดภาพและความกว้างของข้อความในตัวสร้างของมุมมองของฉัน การเปลี่ยนแปลงเล็กน้อยในสองคำตอบของ Jesse นั้นเหมาะกับฉัน:

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

การเปลี่ยนแปลงคือ:

  • การใช้ [NSString sizeWithAttributes]เพื่อรับความกว้างข้อความ
  • รับขนาดภาพโดยตรงUIImageแทนUIImageView

0

มันใช้งานได้ดีสำหรับฉันสำหรับปุ่มหลายปุ่มที่มีความกว้างของภาพที่แตกต่างกันและความยาวชื่อเรื่องที่แตกต่างกัน:

ประเภทรอง 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)
    }

}

0

ทำงานได้ดีสำหรับปุ่มขนาด 80x80 พิกเซล

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

0

ฉันทำการปรับแต่งบางอย่างเพื่อให้ภาพจัดแนวตรงกลางแนวนอน:

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

0

จำเป็นต้องใช้อุปกรณ์เสริมขอบหรือไม่ หากไม่เป็นเช่นนั้นคุณสามารถลองแสดงความเคารพต่อมุมมองพาเรนต์ศูนย์กลาง

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

    }
}

คำถามที่ถามอย่างชัดเจนสำหรับการใช้ imageEdgeInsets และ titleEdgeInsets ดังนั้นจึงเป็นไปได้ว่ามันเป็นข้อบังคับ
Tibrogargan

0

คุณต้องย้ายรูปภาพไปทางขวาตามความกว้างของข้อความ จากนั้นย้ายข้อความไปทางซ้ายตามความกว้างของภาพ

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;

จากนั้นปรับส่วนแทรกด้านบนและด้านล่างเพื่อปรับแกน Y สิ่งนี้สามารถทำได้โดยทางโปรแกรมเช่นกัน แต่ควรมีขนาดคงที่สำหรับขนาดภาพของคุณ ในขณะที่ส่วนแกน X จะต้องเปลี่ยนตามขนาดของฉลากข้อความในแต่ละปุ่ม


0

เพิ่มรหัสนี้ในส่วนขยาย Swift 4.2

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

0

เพียงแค่โยน 2 เซ็นต์ของฉันในนี้ทำงานสำหรับฉัน:

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)
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.