YYText icon indicating copy to clipboard operation
YYText copied to clipboard

UIGraphicsBeginImageContext Deprecated

Open zhuwo opened this issue 2 years ago • 8 comments

大佬,我们这边使用了您的YYText,发现在iOS 17上运行会崩溃,触发了系统的断言: UIGraphicsBeginImageContext() failed to allocate CGBitampContext: size={382, 0}, scale=3.000000, bitmapInfo=0x2002. Use UIGraphicsImageRenderer to avoid this assert.

查了下 api,发现UIGraphicsBeginImageContext在iOS 17上已经deprecated了,

image

大佬是否需要更新下

zhuwo avatar Jun 14 '23 09:06 zhuwo

将 UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale); CGContextRef context = UIGraphicsGetCurrentContext(); if (self.opaque) { CGSize size = self.bounds.size; size.width *= self.contentsScale; size.height *= self.contentsScale; CGContextSaveGState(context); { if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) { CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height)); CGContextFillPath(context); } if (self.backgroundColor) { CGContextSetFillColorWithColor(context, self.backgroundColor); CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height)); CGContextFillPath(context); } } CGContextRestoreGState(context); } task.display(context, self.bounds.size, ^{return NO;}); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.contents = (__bridge id)(image.CGImage);

替换为 UIGraphicsImageRendererFormat *format = [[UIGraphicsImageRendererFormat alloc] init]; format.opaque = self.opaque; format.scale = self.contentsScale;

    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:self.bounds.size format:format];
    UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
        CGContextRef context = rendererContext.CGContext;
        if (self.opaque) {
            CGSize size = self.bounds.size;
            size.width *= self.contentsScale;
            size.height *= self.contentsScale;
            CGContextSaveGState(context); {
                if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) {
                    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
                    CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
                    CGContextFillPath(context);
                }
                if (self.backgroundColor) {
                    CGContextSetFillColorWithColor(context, self.backgroundColor);
                    CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
                    CGContextFillPath(context);
                }
            } CGContextRestoreGState(context);
        }
        task.display(context, self.bounds.size, ^{return NO;});
    }];

    self.contents = (__bridge id)(image.CGImage);

w1090485608 avatar Jun 15 '23 03:06 w1090485608

如果不想像上面一样处理 可以在

  • (void)_displayAsync:(BOOL)async
    这个函数里面添加 if (self.bounds.size.width<=0 || self.bounds.size.height<=0) { self.contents = nil; return; }

CodeSlaveZhang avatar Sep 26 '23 02:09 CodeSlaveZhang

如果不想像上面一样处理 可以在

  • (void)_displayAsync:(BOOL)async 这个函数里面添加 if (self.bounds.size.width<=0 || self.bounds.size.height<=0) { self.contents = nil; return; }

@implementation YYTextAsyncLayer(Hook)

  • (void)load { Method a = class_getInstanceMethod(self, @selector(display)); Method b = class_getInstanceMethod(self, @selector(swizzing_display)); method_exchangeImplementations(a, b); }
  • (void)swizzing_display{ //通过变量名称获取类中的实例成员变量 if (self.bounds.size.width<=0 || self.bounds.size.height<=0) { self.contents = nil; return; } else { [self swizzing_display]; } }

@end

It's better to do this

wanghaolyj avatar Oct 25 '23 08:10 wanghaolyj

Swift的话,采用上面一样的hook方法,在appdelegate方法中调用 YYTextAsyncLayer.swizzleDisplay

` import YYText /// 避免iOS17的崩溃 extension YYTextAsyncLayer {

static let swizzleDisplay: Void = {
    let originalSelector = #selector(display)
    let swizzledSelector = #selector(swizzing_display)
    
    guard let originalMethod = class_getInstanceMethod(YYTextAsyncLayer.self, originalSelector),
          let swizzledMethod = class_getInstanceMethod(YYTextAsyncLayer.self, swizzledSelector) else {
        return
    }
    
    method_exchangeImplementations(originalMethod, swizzledMethod)
}()

@objc func swizzing_display() {
    if bounds.size.width <= 0 || bounds.size.height <= 0 {
        contents = nil
        return
    } else {
        swizzing_display()
    }
}

} `

ZackDT avatar Nov 02 '23 10:11 ZackDT

I do not want to change code in pods. so I create two classe. PPYYLabel, PPYYTextAsyncLayer . user PPYYLabel replace YYLabel


import Foundation
import YYText

///PPYYLabel 代替YYLabel ios17崩溃修复 
class PPYYLabel: YYLabel{
    override class var layerClass: AnyClass {
        return PPYYTextAsyncLayer.self
    }
}



import Foundation
import YYText

/// ios17崩溃修复 
class PPYYTextAsyncLayer: YYTextAsyncLayer {
    override func display() {
        if self.bounds.size.width<=0 || self.bounds.size.height<=0 {
            self.contents = nil
            return
        } else {
            print("---- bounds.width = , ",self.bounds.size.width , " ,height = ",self.bounds.size.height)
            super.display()
        }
    }
}


lizhi0123 avatar Nov 29 '23 11:11 lizhi0123

It seems like the master has retired from the scene for many years. It's unlikely that he will make a comeback again

zhoumingwu avatar Dec 01 '23 03:12 zhoumingwu

这个API还能继续用,只不过当layer的width或height 为0时会触发断言,否则不会。 这个场景在使用AutoLayout时比较常见。

NubiaYin avatar Dec 20 '23 08:12 NubiaYin

如果不想像上面一样处理 可以在

* (void)_displayAsync:(BOOL)async
  这个函数里面添加
  if (self.bounds.size.width<=0 || self.bounds.size.height<=0) {
  self.contents = nil;
  return;
  }

最低支持iOS9的代码,得用这个~

MonicaZhou avatar May 06 '24 09:05 MonicaZhou