ibireme / YYText

Powerful text framework for iOS to display and edit rich text.
MIT License
8.84k stars 1.67k forks source link

UIGraphicsBeginImageContext Deprecated #984

Open zhuwo opened 1 year ago

zhuwo commented 1 year ago

大佬,我们这边使用了您的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

大佬是否需要更新下

w1090485608 commented 1 year ago

将 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);
CodeSlaveZhang commented 9 months ago

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

wanghaolyj commented 8 months ago

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

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

@implementation YYTextAsyncLayer(Hook)

@end

It's better to do this

ZackDT commented 8 months ago

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

} `

lizhi0123 commented 7 months ago

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()
        }
    }
}
zhoumingwu commented 7 months ago

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

NubiaYin commented 6 months ago

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

MonicaZhou commented 2 months ago

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

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

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