CTRunGetImageBounds returns iOS 10 及以下的 x 坐标不正确
CTRunGetImageBounds returns incorrect x coordinate on iOS 10 and below
我正在使用 CoreText 并尝试在 iOS 11/12 上为自定义标签添加 link 支持,但是我也需要支持旧的软件版本。为此,我检查用户的触摸是否与 link 文本边界相交。
为了获得边界,我只需调用 CTRunGetImageBounds(run, context, CFRangeMake(0, 0))
。这在 iOS 11 和 12 上完美运行,但在 9 和 10 上坏了。我在 iOS 11 上沿着 (250,8.1,100,30) 的线返回了一个矩形,但在 [=23 上=]9 然而,同样的函数调用returns(0.1,8.1,100,30)。 x 坐标似乎是相对于 运行 而不是实际框架。由于坐标无效,无法在 iOS 9/10 上正确单击 links,这是一个显而易见的问题。
我附上了一张比较两者的图片。预期的行为是 links 应该用绿色矩形突出显示。请注意,iOS 12 模拟器正确执行此操作,而 9.3 由于 x 坐标接近零,所有矩形都向左猛击。
这似乎是 CoreText 错误或 Apple 在没有记录的情况下更改的某些行为。在 iOS 9 CTRunGetImageBounds
returns 上,相对于自身的 x 值,而不是更大的框架。这意味着虽然高度和宽度正确,但返回的 x 坐标根本没用。由于这是一个早已失效的固件,它不会得到修复,但我还是找到了解决方法!
Swift 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
returns 一个无效的 x 坐标,我们可以使用 CTRunGetPositionsPtr
来修复它,因为它 returns 正确的 origin 为 运行。为了获得范围的完整、正确的框架,我们只需要用我们的新值替换 CTRunGetImageBounds
中的 x 值。
这可能会慢一点,但只会慢几分之一毫秒。
我正在使用 CoreText 并尝试在 iOS 11/12 上为自定义标签添加 link 支持,但是我也需要支持旧的软件版本。为此,我检查用户的触摸是否与 link 文本边界相交。
为了获得边界,我只需调用 CTRunGetImageBounds(run, context, CFRangeMake(0, 0))
。这在 iOS 11 和 12 上完美运行,但在 9 和 10 上坏了。我在 iOS 11 上沿着 (250,8.1,100,30) 的线返回了一个矩形,但在 [=23 上=]9 然而,同样的函数调用returns(0.1,8.1,100,30)。 x 坐标似乎是相对于 运行 而不是实际框架。由于坐标无效,无法在 iOS 9/10 上正确单击 links,这是一个显而易见的问题。
我附上了一张比较两者的图片。预期的行为是 links 应该用绿色矩形突出显示。请注意,iOS 12 模拟器正确执行此操作,而 9.3 由于 x 坐标接近零,所有矩形都向左猛击。
这似乎是 CoreText 错误或 Apple 在没有记录的情况下更改的某些行为。在 iOS 9 CTRunGetImageBounds
returns 上,相对于自身的 x 值,而不是更大的框架。这意味着虽然高度和宽度正确,但返回的 x 坐标根本没用。由于这是一个早已失效的固件,它不会得到修复,但我还是找到了解决方法!
Swift 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
returns 一个无效的 x 坐标,我们可以使用 CTRunGetPositionsPtr
来修复它,因为它 returns 正确的 origin 为 运行。为了获得范围的完整、正确的框架,我们只需要用我们的新值替换 CTRunGetImageBounds
中的 x 值。
这可能会慢一点,但只会慢几分之一毫秒。