[Iphone] محاذاة النص والصورة على UIButton مع imageEdgeInsets و titleEdgeInsets


Answers

أنا متأخرة قليلاً لهذا الحزب ، لكن أعتقد أن لدي شيء مفيد لإضافته.

إجابة Kekoa رائعة ، ولكن كما يذكر RonLugge ، يمكن أن يجعل الزر لا يعد احترام sizeToFit ، أو الأهم من ذلك ، يمكن أن يسبب الزر sizeToFit محتواه عندما يكون حجمه جوهريا. ييكيس!

أولا ، مع ذلك ،

شرح موجز عن كيفية عمل titleEdgeInsets و titleEdgeInsets :

تحتوي المستندات الخاصة بـ imageEdgeInsets على ما يلي ، جزئيًا:

استخدم هذه الخاصية لتغيير حجم وإعادة وضع مستطيل الرسم الفعال لصورة الزر. يمكنك تحديد قيمة مختلفة لكل من الأشكال الأربعة (أعلى ، يسار ، أسفل ، يمين). تنكمش قيمة موجبة ، أو insets ، تلك الحافة — مما يجعلها أقرب إلى وسط الزر. تقوم قيمة سالبة بتوسيع هذه الحافة أو بداياتها.

أعتقد أن هذه الوثائق كتبت بتخيل أن الزر ليس له عنوان ، بل مجرد صورة. يجعل التفكير أكثر بهذه الطريقة ، ويتصرف كيفية UIEdgeInsets عادةً. بشكل أساسي ، يتم نقل إطار الصورة (أو العنوان ، بالعنوان titleEdgeInsets ) إلى الداخل titleEdgeInsets السلبية.

حسنا ، ماذا في ذلك؟

أنا أصل إلى هناك! إليك ما لديك بشكل افتراضي ، تعيين صورة وعنوان (حدود الزر باللون الأخضر فقط لإظهار مكانها):

عندما تريد التباعد بين الصورة والعنوان ، دون التسبب في أي سحق ، تحتاج إلى تعيين أربعة إدخالات مختلفة ، اثنان على كل من الصورة والعنوان. هذا لأنك لا ترغب في تغيير أحجام إطارات تلك العناصر ، ولكن فقط لوضعها. عندما تبدأ بالتفكير بهذه الطريقة ، يصبح التغيير المطلوب لفئة Kekoa الممتازة واضحا:

@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

لكن انتظر ، قل ، عندما أفعل ذلك ، أحصل على هذا:

آه أجل! لقد نسيت ، لقد حذرتني المستندات من ذلك. يقولون ، في جزء منه:

يتم استخدام هذه الخاصية فقط لتحديد موقع الصورة أثناء التخطيط. لا يستخدم الزر هذه الخاصية لتحديد intrinsicContentSize و sizeThatFits:

ولكن هناك خاصية يمكن أن تساعد ، وهذا هو contentEdgeInsets . المستندات لهذا القول ، في جزء منه:

يستخدم الزر هذه الخاصية لتحديد intrinsicContentSize و sizeThatFits:

هذا يبدو جيدا. لذلك دعونا نقوم بتعديل الفئة مرة أخرى:

@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

وماذا تحصل؟

يبدو وكأنه الفائز لي.

العمل في سويفت ولا تريد القيام بأي تفكير على الإطلاق؟ إليك الإصدار النهائي من الإضافة في 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)
    }
}
Question

أود وضع رمز على يسار سطرين من النص بحيث يكون هناك حوالي 2-3 بكسل من المساحة بين الصورة وبداية النص. عنصر التحكم نفسه هو مركز محاذاة أفقيًا (تم تعيينه من خلال واجهة منشئ)

سيشبه هذا الزر شيئًا كالتالي:

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

أحاول تكوين هذا مع contentEdgeInset و imageEdgeInsets و titleEdgeInsets دون جدوى. أتفهم أن القيمة السالبة توسع الحافة بينما تقللها القيمة الإيجابية لنقلها إلى المركز أقرب.

حاولت:

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

لكن هذا لا يعرضها بشكل صحيح. لقد قمت بتعديل القيم لكن الانتقال من القول -5 إلى -10 على القيمة الداخلية اليسرى لا يبدو أنه يتحرك بطريقة متوقعة. -10 سوف يسحبون النص على طول الطريق إلى اليسار لذا كنت أتوقع -5 أن أسحره في منتصف الطريق من الجانب الأيسر لكنه لا.

ما هو المنطق وراء الدفاتر؟ لست على دراية بمواضع الصور والمصطلحات ذات الصلة.

لقد استخدمت سؤال SO كمرجع لكن شيئًا عن قيمي ليس صحيحًا. UIButton: كيفية توسيط صورة ونص باستخدام imageEdgeInsets و titleEdgeInsets؟




أنا متأخرة قليلا لهذا الحزب أيضا ، ولكن أعتقد أن لدي شيء مفيد لإضافة: س).

لقد أنشأت فئة فرعية من UIButton الغرض منها هو أن يكون قادرا على اختيار مكان صورة الزر ، إما رأسيًا أو أفقيًا.

هذا يعني أنه يمكنك عمل هذا النوع من الأزرار:

هنا التفاصيل حول كيفية إنشاء هذه الأزرار مع صفي:

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)

ولإجراء ذلك ، أضفت 2 من السمات: imageVerticalAlignment و imageHorizontalAlignment . بالطبع ، إذا كان الزر الخاص بك يحتوي فقط على صورة أو عنوان ... لا تستخدم هذا الفصل على الإطلاق!

أضفت أيضًا سمة تسمى imageToTitleSpacing والتي تسمح لك بتعديل المسافة بين العنوان والصورة.

يحاول هذا الفصل جهده ليكون متوافقًا إذا كنت تريد استخدام imageEdgeInsets و titleEdgeInsets و titleEdgeInsets مباشرة أو في combinaison مع سمات التنسيق الجديدة.

كما يشرح لناravron ، أبذل قصارى جهدي لجعل حافة محتوى الزر صحيحة (كما ترون بالحدود الحمراء).

يمكنك أيضًا استخدامه في Interface Builder:

  1. إنشاء UIButton
  2. تغيير فئة الزر
  3. ضبط السمات القابلة للتنسيق باستخدام "center" أو "top" أو "bottom" أو "left" أو "right"

هنا الكود ( gist ):

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



أيضا إذا كنت تريد أن تجعل شيء مماثل ل

انت تحتاج

1. ضبط المحاذاة الأفقية والعمودية للزر ل

  1. البحث عن جميع القيم المطلوبة وتعيين 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)];
    

سيقوم هذا بترتيب العنوان والصورة على الزر.

كما يرجى ملاحظة تحديث هذا على كل relayout

سريع

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



إضافة صغيرة إلى Riley Avron للإجابة على التغييرات في لغة الحساب:

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



في ما يلي مثال بسيط لكيفية استخدام imageEdgeInsets سيؤدي هذا إلى إنشاء زر 30x30 بمساحة أكبر بـ 10 بيكسلات على طول الطريق (50 × 50)

    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)



Links