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];
KONG
źródło
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)]];
gcamp
źródło
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.

Dave
źródło
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

nalexn
źródło
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

Tanguy G.
źródło
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);
ninjaneer
źródło
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

capikaw
źródło
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.
Ortwin Gentz
źródło
8

Jeśli chcesz po prostu zmniejszyć obraz przycisku:

yourButton.contentMode = UIViewContentModeScaleAspectFit;
yourButton.imageEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10);
Tulleb
źródło
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

Hamid Reza Ansari
źródło
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;
}
Abedalkareem Omreyh
źródło
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.

Rudolf Adamkovič
źródło
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

Andrzej
źródło
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;
}
Andy Poes
źródło
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
Qasim Mirza
źródło
0

W przypadku platformy Xamarin.iOS (C #):

    myButton.VerticalAlignment = UIControlContentVerticalAlignment.Fill;
    myButton.HorizontalAlignment = UIControlContentHorizontalAlignment.Fill;
    myButton.ImageView.ContentMode = UIViewContentMode.ScaleAspectFit;
Maciej
źródło
-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.

ruyamonis346
źródło
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