przeskalować obraz w UIButton do AspectFit?


97

Chcę dodać obraz do UIButton, a także chcę przeskalować mój obraz, aby pasował do UIButton (pomniejszyć obraz). Pokaż mi, proszę, jak to zrobić.

Oto, czego próbowałem, ale to nie działa:

  • Dodanie obrazu do przycisku i użycie setContentMode:
[self.itemImageButton setImage:stretchImage forState:UIControlStateNormal];
[self.itemImageButton setContentMode:UIViewContentModeScaleAspectFit];
  • Tworzenie „rozciągniętego obrazu”:
UIImage *stretchImage = [updatedItem.thumbnail stretchableImageWithLeftCapWidth:0 topCapHeight:0];

Myślę, że zostało to zmienione na domyślne zachowanie w oprogramowaniu sprzętowym 4.0 i nowszych.
Shaun Budhram

1
Rozwiązanie znajdziesz na stackoverflow.com/a/28205566/481207 .
Matt

Odpowiedzi:


28

Jeśli naprawdę chcesz przeskalować obraz, zrób to, ale przed użyciem zmień jego rozmiar. Zmiana rozmiaru w czasie wykonywania spowoduje po prostu utratę cykli procesora.

Oto kategoria, której używam do skalowania obrazu:

UIImage + Extra.h

@interface UIImage (Extras)
- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize;
@end;

UIImage + Extra.m

@implementation UIImage (Extras)

- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize {

UIImage *sourceImage = self;
UIImage *newImage = nil;

CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;

CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;

CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;

CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

if (!CGSizeEqualToSize(imageSize, targetSize)) {

        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;

        if (widthFactor < heightFactor) 
                scaleFactor = widthFactor;
        else
                scaleFactor = heightFactor;

        scaledWidth  = width * scaleFactor;
        scaledHeight = height * scaleFactor;

        // center the image

        if (widthFactor < heightFactor) {
                thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; 
        } else if (widthFactor > heightFactor) {
                thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
        }
}


// this is actually the interesting part:

UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0);

CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width  = scaledWidth;
thumbnailRect.size.height = scaledHeight;

[sourceImage drawInRect:thumbnailRect];

newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

if(newImage == nil) NSLog(@"could not scale image");


return newImage ;
}

@end

Możesz go użyć do żądanego rozmiaru. Lubić :

[self.itemImageButton setImage:[stretchImage imageByScalingProportionallyToSize:CGSizeMake(20,20)]];

Dzięki za odpowiedź, Gcamp. Miałem metodę zmiany rozmiaru według proporcji. Ale wciąż próbuję znaleźć sposób, by powiedzieć UIButtonowi, żeby zrobił to za mnie. Mój obraz jest ładowany z Internetu, więc nie mogę zmienić jego rozmiaru w czasie kompilacji. Przy okazji, bardzo dziękuję za twoją odpowiedź.
KONG

2
Ponieważ obraz jest umieszczony w CALayer, renderowany i przechowywany w pamięci podręcznej przez Quartz, wydajność nie powinna być gorsza, jeśli umieścisz go tam w pełnym rozmiarze. Tak czy inaczej, zmieniasz rozmiar ~ 1 raz. Odpowiedź gcamp jest o wiele ważniejsza, jeśli ciągle zmieniasz rozmiar przycisku lub robisz inne rzeczy, które spowodowałyby ponowne narysowanie warstwy obrazu przez Quartz.
Ben Gotow

wydaje się, że jakość obrazu jest obniżona. testuję na iPhonie 6 plus. Czy to prawda? Mam też zdjęcie 3x.
Khant Thu Linn

Tak, ten kod jest dość stary. To było UIGraphicsBeginImageContextzamiast UIGraphicsBeginImageContextWithOptions. Właśnie to naprawiłem.
gcamp

tak, ale w wielu przypadkach nie ma znaczenia, czy stracisz kilka cykli, więc nie ma sensu napełniać barana
Radu Simionescu

204

Miałem ten sam problem. Wystarczy ustawić ContentMode ImageView, który znajduje się wewnątrz UIButton.

[[self.itemImageButton imageView] setContentMode: UIViewContentModeScaleAspectFit];
[self.itemImageButton setImage:[UIImage imageNamed:stretchImage] forState:UIControlStateNormal];

Mam nadzieję że to pomoże.


14
Upewnij się, że ustawiasz imageView . Przez chwilę nie zauważyłem też, że nadal korzystam z backgroundImageView i nie działa na to.
Alexandre Gomes

2
Wygląda na to, że ta metoda ma pewne problemy, gdy stan przycisku jest „podświetlony”. Obraz powróci do trybu wypełnienia. Dowolny pomysł?
Yuanfei Zhu

Na mnie to nie działa. Jednak sprawiam, że to działa, używając [self.itemImageButton setbackgroundImage: [UIImage imageNamed: stretchImage] forState: UIControlStateNormal];
Mickey,

3
To zadziałało dla mnie. Ustawiłem tryb treści jak wyżej. Należy jednak również ustawić ten sam obraz w stanie podświetlenia, aby uniknąć powrotu obrazu do „trybu wypełnienia”. Na przykład [imageButton setImage: image forState: UIControlStateHighlighted];
Christopher

1
Możesz to zrobić w programie Interface Builder ze ścieżką klucza imageView.contentMode, typem Numberi wartością1
bendytree

109

Żadna z odpowiedzi tutaj naprawdę nie zadziałała, rozwiązałem problem za pomocą następującego kodu:

button.contentMode = UIViewContentModeScaleToFill;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;

Możesz to również zrobić w programie Interface Builder.

wprowadź opis obrazu tutaj


5
To nie zapewnia zachowania dopasowania aspektu, o które prosi OP.
Ortwin Gentz

3
@OrtwinGentz ​​możesz wybrać tryb i ustawić Aspect fitzamiast scale to fill.
Daniel

55

Najłatwiejszy sposób na programowe ustawienie UIButton imageView w trybie dopasowania proporcji :

Szybki

button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.imageView?.contentMode = .scaleAspectFit

Cel C

button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
button.imageView.contentMode = UIViewContentModeScaleAspectFit;

Uwaga: możesz zmienić .scaleAspectFit (UIViewContentModeScaleAspectFit) na .scaleAspectFill (UIViewContentModeScaleAspectFill), aby ustawić tryb wypełniania proporcji


20

Miałem problemy z nieproporcjonalną zmianą rozmiaru obrazu, więc naprawiłem to za pomocą wstawek krawędziowych.

fooButton.contentEdgeInsets = UIEdgeInsetsMake(10, 15, 10, 15);

16

Można to teraz zrobić za pomocą właściwości UIButton IB. Kluczem jest ustawienie obrazu jako tła, w przeciwnym razie to nie zadziała.

wprowadź opis obrazu tutaj


14

Rozwijając odpowiedź Dave'a, możesz ustawić wszystkie contentModeprzyciski imageVieww IB, bez żadnego kodu, używając atrybutów środowiska wykonawczego:

wprowadź opis obrazu tutaj

  • 1znaczy UIViewContentModeScaleAspectFit,
  • 2znaczyłoby UIViewContentModeScaleAspectFill.

8

Jeśli chcesz po prostu zmniejszyć obraz przycisku:

yourButton.contentMode = UIViewContentModeScaleAspectFit;
yourButton.imageEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10);

Powinno to być yourButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
sdsykes

Nie próbowałem z .imageView. Działa bez całkiem dobrze.
Tulleb

6

1 - wyczyść domyślny tekst przycisku (ważne)

2 - ustaw wyrównanie jak obraz

3 - ustaw tryb treści, taki jak obraz

wprowadź opis obrazu tutaj


Dobra odpowiedź, stary… bardzo mi pomogła. Dzięki!
BharathRao

5

Mam metodę, która robi to za mnie. Metoda przyjmuje UIButtoni dopasowuje proporcje obrazu.

-(void)makeImageAspectFitForButton:(UIButton*)button{
    button.imageView.contentMode=UIViewContentModeScaleAspectFit;
    button.contentHorizontalAlignment=UIControlContentHorizontalAlignmentFill;
    button.contentVerticalAlignment=UIControlContentVerticalAlignmentFill;
}

4

Najczystszym rozwiązaniem jest użycie Auto Layout . Spuściłem Content Compression Resistance Priorytet z moich UIButtoni ustawić obraz (nie Tło obrazu ) za pośrednictwem interfejsu Builder . Potem dodałem kilka ograniczeń, które definiują rozmiar mojego przycisku (dość skomplikowanego w moim przypadku) i działało to jak urok.


U mnie to zadziałało - ale tylko wtedy, gdy wybrałem opcję „Obraz tła”, a nie rzeczywisty obraz. Cokolwiek działa! To doprowadzało mnie do NUTS.
Andy

W Xcode 6.2 działało to dla mnie, ale jak powiedział @AndrewHeinlein, używaneBackground Image
RoLYroLLs

3

upewnij się, że ustawiłeś obraz na właściwość Image, ale nie na Background


2

Obraz tła można w rzeczywistości ustawić tak, aby dość łatwo skalował wypełnienie według aspektu. Wystarczy zrobić coś takiego w podklasie UIButton:

- (CGRect)backgroundRectForBounds:(CGRect)bounds
{
    // you'll need the original size of the image, you 
    // can save it from setBackgroundImage:forControlState
    return CGRectFitToFillRect(__original_image_frame_size__, bounds);
}

// Utility function, can be saved elsewhere
CGRect CGRectFitToFillRect( CGRect inRect, CGRect maxRect )
{
    CGFloat origRes = inRect.size.width / inRect.size.height;
    CGFloat newRes = maxRect.size.width / maxRect.size.height;

    CGRect retRect = maxRect;

    if (newRes < origRes)
    {
        retRect.size.width = inRect.size.width * maxRect.size.height / inRect.size.height;
        retRect.origin.x = roundf((maxRect.size.width - retRect.size.width) / 2);
    }
    else
    {
        retRect.size.height = inRect.size.height * maxRect.size.width / inRect.size.width;
        retRect.origin.y = roundf((maxRect.size.height - retRect.size.height) / 2);
    }

    return retRect;
}

Czy nie jest to możliwe bez podklasy?
Iulian Onofrei

Ta odpowiedź dotyczyła w szczególności backgroundImage. W przypadku głównego obrazu można małpować za pomocą elementu imageEdgeInsets.
Andy Poes

Wiem i chciałem, żeby można to osiągnąć bez podklasy.
Iulian Onofrei

O ile wiem, nie można dostosować backgroundImage bez podklasy.
Andy Poes

1

Swift 5.0

 myButton2.contentMode = .scaleAspectFit
 myButton2.contentHorizontalAlignment = .fill
 myButton2.contentVerticalAlignment = .fill

0

W przypadku platformy Xamarin.iOS (C #):

    myButton.VerticalAlignment = UIControlContentVerticalAlignment.Fill;
    myButton.HorizontalAlignment = UIControlContentHorizontalAlignment.Fill;
    myButton.ImageView.ContentMode = UIViewContentMode.ScaleAspectFit;

-1

Wystarczy ustawić tryb treści widoku obrazu UIButton dla trzech zdarzeń. -

[cell.button setImage:[UIImage imageWithData:data] forState:UIControlStateNormal];

[cell.button setImage:[UIImage imageWithData:data] forState:UIControlStateHighlighted];

[cell.imgIcon setImage:[UIImage imageWithData:data] forState:UIControlStateSelected];

Mamy kod dla trzech zdarzeń bcoz podczas podświetlania lub wybierania, jeśli rozmiar przycisku to KWADRAT, a rozmiar obrazu to prostokąt, to pokaże kwadratowy obraz w momencie podświetlania lub wybierania.

Jestem pewien, że to zadziała.


Nie ustawiasz trybu treści, tylko ustawiasz obraz. I to nie są wydarzenia, to stany. Robisz duży bałagan. To jest zupełnie niezwiązane z pytaniem.
manecosta
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.