CTRunGetImageBounds在iOS 10及更低版本上返回不正确的x坐标

我正在使用CoreText,我正在尝试为iOS 11/12上的自定义标签添加链接支持,但我也需要支持更旧的软件版本。 为此,我检查用户的触摸是否相交以及链接文本边界。

为了得到界限,我只需调用CTRunGetImageBounds(run, context, CFRangeMake(0, 0)) 。 这在iOS 11和12上运行完美,但是在9和10上断了。我在iOS 11上得到了一条直线(250,8.1,100,30),但是在iOS 9上,相同的函数调用返回( 0.1,8.1,100,30)。 x坐标似乎是相对于运行而不是实际帧。 由于坐标无效,因此无法在iOS 9/10上正确点击链接,这是一个明显原因的问题。

我列出了一张比较两者的图片。 预期的行为是链接应该用绿色矩形突出显示。 请注意,iOS 12模拟器正确执行此操作,而9.3由于x坐标接近零而使所有矩形都向左猛击。

iOS 12模拟器 iOS 9.3模拟器

修复了iOS 9.3

这似乎是一个CoreText错误或Apple没有记录的一些行为。 在iOS 9上, CTRunGetImageBounds返回相对于自身的x值,而不是更大的帧。 这意味着当高度和宽度正确时,返回的x坐标根本没用。 由于这是一个长期死的固件,它不会得到修复,但我发现一个工作无论如何!

斯威夫特4

 /// Get the CoreText relative frame for a given CTRun. This method works around an iOS <=10 CoreText bug in CTRunGetImageBounds /// /// - Parameters: /// - run: The run /// - context: Context, used by CTRunGetImageBounds /// - Returns: A tight fitting, CT rect that fits around the run func getCTRectFor(run:CTRun,context:CGContext) -> CGRect { let imageBounds = CTRunGetImageBounds(run, context, CFRangeMake(0, 0)) if #available(iOS 11.0, *) { //Non-bugged iOS, can assume the bounds are correct return imageBounds } else { //<=iOS 10 has a bug with getting the frame of a run where it gives invalid x positions //The CTRunGetPositionsPtr however works as expected and returns the correct position. We can take that value and substitute it let runPositionsPointer = CTRunGetPositionsPtr(run) if let runPosition = runPositionsPointer?.pointee { return CGRect.init(x: runPosition.x, y: imageBounds.origin.y, width: imageBounds.width, height: imageBounds.height) }else { //FAILED TO OBTAIN RUN ORIGIN? FALL BACK. return imageBounds } } } 

CTRunGetImageBounds返回无效的x坐标时,我们可以使用CTRunGetPositionsPtr来修复它,因为它返回了运行的正确原点 。 要获得范围的完整,正确的帧,我们只需要使用新值替换CTRunGetImageBounds的x值。

这可能会稍微慢一些,但只需要几分之一毫秒。