Jak ustawić krycie / alfa UIImage?

83

Wiem, że możesz to zrobić za pomocą UIImageView, ale czy można to zrobić z UIImage? Chcę, aby właściwość tablicy animacji obrazów UIImageView była tablicą tego samego obrazu, ale z różnymi nieprzezroczystościami. Myśli?

Marty
źródło

Odpowiedzi:

122

Po prostu musiałem to zrobić, ale pomyślałem, że rozwiązanie Stevena będzie powolne. Miejmy nadzieję, że powinno to wykorzystywać sprzęt graficzny. Utwórz kategorię na UIImage:

- (UIImage *)imageByApplyingAlpha:(CGFloat) alpha {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGRect area = CGRectMake(0, 0, self.size.width, self.size.height);

    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -area.size.height);

    CGContextSetBlendMode(ctx, kCGBlendModeMultiply);

    CGContextSetAlpha(ctx, alpha);

    CGContextDrawImage(ctx, area, self.CGImage);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return newImage;
}
Nick H247
źródło
3
nie pamiętam nawet, dlaczego tego potrzebowałem :)
Marty
Świetne rozwiązanie! Dziękuję Nick!
Christopher
1
Po prostu upewnij się, że wywołujesz UIGraphicsBeginImageContextWithOptions w głównym wątku, ponieważ renderowanie w tle będzie nieprzewidywalne.
Steven Veltema
1
Według Apple możesz wywołać UIGraphicsBeginImageContextWithOptions w dowolnym wątku od iOS 4 i nowszych.
Ants,
6
Działa świetnie, wielkie dzięki. Dla innych początkujących w logistyce związanej z rozwojem iOS, oto kroki, aby utworzyć kategorię dla UIImage. 1. Kliknij prawym przyciskiem myszy dyrektora projektu i wybierz Nowy plik. 2. Na wyświetlonym ekranie wybierz typ pliku kategorii Cel-C. 3. Dodaj kod odpowiedzi w pliku .m oraz deklarację metody w pliku .h. 4. W pliku, w którym obraz ma być przezroczysty, # zaimportuj plik. 5. Użyj metody imageByApplyingAlpha na instancji UIImage.
Danny
79

Ustaw krycie widoku, w którym jest on wyświetlany.

UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithName:@"SomeName.png"]];
imageView.alpha = 0.5; //Alpha runs from 0.0 to 1.0

Użyj tego w animacji. Możesz zmienić wartość alfa w animacji na pewien czas.

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
//Set alpha
[UIView commitAnimations];
Mats Stijlaart
źródło
tak, pomyślałem, że być może będę musiał to zrobić w ten sposób z zegarem, aby stale zmieniać krycie, po prostu pomyślałem, że znacznie łatwiej byłoby mieć tablicę dla właściwości tablicy obrazów animacji, aby animowała się samodzielnie.
Marty
Możesz animować „wchodzenie” i „wychodzenie” jak bicie serca z delegatem animacji. Nie używaj timera, animacja płynnie zmieni alfa. Powodzenia.
Mats Stijlaart
3
Działa to tylko wtedy, gdy deweloper używa UIImageView. Pytanie wyraźnie mówi, że tak nie jest.
Raul Huerta
1
Działa to z UIImage prezentowanym w CALayer, tylko Ty ustawiasz layer.opacity bez konieczności modyfikowania obrazu. Dzięki Mats!
Chris
Świetny! Działa UIButtondokładnie to, czego potrzebowałem.
NecipAllef
53

Na podstawie odpowiedzi Alexey Ishkov, ale w Swift

Użyłem rozszerzenia klasy UIImage.

Swift 2:

Rozszerzenie UIImage:

extension UIImage {
    func imageWithAlpha(alpha: CGFloat) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        drawAtPoint(CGPointZero, blendMode: .Normal, alpha: alpha)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
}

Używać:

let image = UIImage(named: "my_image")
let transparentImage = image.imageWithAlpha(0.5)

Swift 3/4/5:

Zauważ, że ta implementacja zwraca opcjonalny UIImage. Dzieje się tak, ponieważ w Swift 3 UIGraphicsGetImageFromCurrentImageContextzwraca teraz opcjonalny. Ta wartość może wynosić zero, jeśli kontekst jest zerowy lub nie został utworzony za pomocą UIGraphicsBeginImageContext.

Rozszerzenie UIImage:

extension UIImage {
    func image(alpha: CGFloat) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        draw(at: .zero, blendMode: .normal, alpha: alpha)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
}

Używać:

let image = UIImage(named: "my_image")
let transparentImage = image?.image(alpha: 0.5)
Devin B.
źródło
Wolę tę Swift 3wersję.
AechoLiu
Dzięki. Działa tak samo w Swift 4, zaktualizowałem odpowiedź, aby to odzwierciedlić.
zeeshan
Bravo sir, bravo
Ryan Brodie
17

jest dużo prostsze rozwiązanie:

- (UIImage *)tranlucentWithAlpha:(CGFloat)alpha
{
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    [self drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:alpha];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
Alexey Ishkov
źródło
1
Prawdopodobnie powinna być zaakceptowana odpowiedź. Najszybszy / najbardziej efektywny sposób na uzyskanie wyniku
Will Von Ullrich
4

Zdaję sobie sprawę, że to dość późno, ale potrzebowałem czegoś takiego, więc wymyśliłem szybką i brudną metodę, aby to zrobić.

+ (UIImage *) image:(UIImage *)image withAlpha:(CGFloat)alpha{

    // Create a pixel buffer in an easy to use format
    CGImageRef imageRef = [image CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    UInt8 * m_PixelBuf = malloc(sizeof(UInt8) * height * width * 4);

    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(m_PixelBuf, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);

    //alter the alpha
    int length = height * width * 4;
    for (int i=0; i<length; i+=4)
    {
        m_PixelBuf[i+3] =  255*alpha;
    }


    //create a new image
    CGContextRef ctx = CGBitmapContextCreate(m_PixelBuf, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    CGImageRef newImgRef = CGBitmapContextCreateImage(ctx);  
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(ctx);  
    free(m_PixelBuf);

    UIImage *finalImage = [UIImage imageWithCGImage:newImgRef];
    CGImageRelease(newImgRef);  

    return finalImage;
}
Steven Veltema
źródło
2
Lepsza byłaby nazwasetImage:withAlpha:
Alexander
8
set zwykle odnoszą się do właściwości, w jakiś sposób zmieniając stan odbiornika. lepiej by to nazwać image:withAlpha:?
Jonathan.
2
Tak. Również wywołanie set powoduje ustawienie tego samego przekazanego obiektu, zamiast zwracania nowego obrazu.
system
4

Hej, hej, dzięki od użytkownika Xamarin! :) Tutaj jest przetłumaczone na c #

//***************************************************************************
public static class ImageExtensions
//***************************************************************************
{
    //-------------------------------------------------------------
    public static UIImage WithAlpha(this UIImage image, float alpha)  
    //-------------------------------------------------------------
        {
        UIGraphics.BeginImageContextWithOptions(image.Size,false,image.CurrentScale);
        image.Draw(CGPoint.Empty, CGBlendMode.Normal, alpha);
        var newImage = UIGraphics.GetImageFromCurrentImageContext();
        UIGraphics.EndImageContext();
        return newImage;
        }

}

Przykład użycia:

var MySupaImage = UIImage.FromBundle("opaquestuff.png").WithAlpha(0.15f);
Nick Kovalsky
źródło
1

taki sam wynik jak inne, inny styl:

extension UIImage {

    func withAlpha(_ alpha: CGFloat) -> UIImage {
        return UIGraphicsImageRenderer(size: size).image { _ in
            draw(at: .zero, blendMode: .normal, alpha: alpha)
        }
    }

}
Devin Pitcher
źródło
1

Swift 5:

extension UIImage {
  func withAlphaComponent(_ alpha: CGFloat) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(size, false, scale)
    defer { UIGraphicsEndImageContext() }

    draw(at: .zero, blendMode: .normal, alpha: alpha)
    return UIGraphicsGetImageFromCurrentImageContext()
  }
}
Vadoff
źródło
0

Jeśli eksperymentujesz z renderowaniem metalu i wyodrębniasz CGImage wygenerowany przez imageByApplyingAlpha w pierwszej odpowiedzi, możesz skończyć z renderowaniem metalu, które jest większe niż się spodziewasz. Podczas eksperymentowania z Metal możesz chcieć zmienić jedną linię kodu w imageByApplyingAlpha:

    UIGraphicsBeginImageContextWithOptions (self.size, NO, 1.0f);
//  UIGraphicsBeginImageContextWithOptions (self.size, NO, 0.0f);

Jeśli używasz urządzenia o współczynniku skali 3,0, takim jak iPhone 11 Pro Max, współczynnik skali 0,0 pokazany powyżej da ci obraz CG, który jest trzy razy większy niż się spodziewasz. Zmiana współczynnika skalowania na 1,0 powinna uniknąć skalowania.

Miejmy nadzieję, że ta odpowiedź zaoszczędzi początkującym wielu irytacji.

Russes
źródło