这是 UITextView 透明度错误吗?
Is this a UITextView transparency bug?
此问题与 相关,我应该可以创建解决方法。随着我进一步调查,我发现它比我原先想象的更广泛。我之前只在至少包含一个换行符的显示文本中注意到它,但下面的情况并非如此。
问题似乎是由于使用 NSLayoutManager 的 boundingRect 方法获取(除其他外)单个字符宽度,然后使用这些宽度设置字符的 UITextView 框架宽度属性。这样做显然会导致文本视图的 backgroundColor 设置为 UIColor.clear 被忽略(即背景变得不透明)。下面的 Playground 代码重现了问题,如 red text 所示,并在 black 中显示了使用宽度常量的解决方法。字距调整得越紧,效果越明显。
这是一个错误吗?还是因为其他原因导致的怪癖?
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.bounds = CGRect(x: -100, y: -100, width: 200, height: 200)
view.backgroundColor = .white
let str = "..T.V.W.Y.."
let strStorage = NSTextStorage(string: str)
let layoutManager = NSLayoutManager()
strStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: view.bounds.size)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
let strArray = Array(str)
struct CharInfo {
var char: Character
var origin: CGPoint?
var size: CGSize?
}
var charInfoArray = [CharInfo]()
for index in 0..<str.count {
charInfoArray.append(CharInfo.init(char: strArray[index], origin: nil, size: nil))
let charRange = NSMakeRange(index, 1)
let charRect = layoutManager.boundingRect(forGlyphRange: charRange, in: textContainer)
charInfoArray[index].origin = charRect.origin
charInfoArray[index].size = charRect.size
}
for charInfo in charInfoArray {
let textView0 = UITextView()
textView0.backgroundColor = UIColor.clear // Ignored in this case!!
textView0.text = String(charInfo.char)
textView0.textContainerInset = UIEdgeInsets.zero
let size0 = charInfo.size!
textView0.frame = CGRect(origin: charInfo.origin!, size: size0)
textView0.textContainer.lineFragmentPadding = CGFloat(0.0)
textView0.textColor = UIColor.red
view.addSubview(textView0)
let textView1 = UITextView()
textView1.backgroundColor = UIColor.clear // Required
textView1.text = String(charInfo.char)
textView1.textContainerInset = UIEdgeInsets.zero
var size1 = charInfo.size!
size1.width = 20 // But changing .height has no effect on opacity
textView1.frame = CGRect(origin: charInfo.origin!, size: size1)
textView1.frame = textView1.frame.offsetBy(dx: 0, dy: 20)
textView1.textContainer.lineFragmentPadding = CGFloat(0.0)
textView1.textColor = UIColor.black
view.addSubview(textView1)
}
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
这看起来确实是一个错误,但它与 NSLayoutManager 的实例方法 boundingRect(forGlyphRange:in:) 有关。它只是看起来可能是透明度变化。
根据 Apple's documentation,boundingRect(forGlyphRange:in:) 应该是“[return] 一个单一的边界矩形(在容器坐标中)包含所有字形和在给定中绘制的其他标记给定字形范围的文本容器,包括绘制在其线段矩形之外的字形和下划线等文本属性。”但这不是它正在做的。
在这种情况下,由于字距调整,每个 boundingRect 的宽度减少了下一个字形向左移动的量。您可以对此进行测试,例如,使用 str = "ToT" 并在设置后立即添加 print(size0.width) 。你会得到这个:
6.0 // "T"; should have been 7.330078125
6.673828125 // "o"
7.330078125 // "T"
在修复此错误之前,解决方法是单独计算每个字符的字形大小。
此问题与
问题似乎是由于使用 NSLayoutManager 的 boundingRect 方法获取(除其他外)单个字符宽度,然后使用这些宽度设置字符的 UITextView 框架宽度属性。这样做显然会导致文本视图的 backgroundColor 设置为 UIColor.clear 被忽略(即背景变得不透明)。下面的 Playground 代码重现了问题,如 red text 所示,并在 black 中显示了使用宽度常量的解决方法。字距调整得越紧,效果越明显。
这是一个错误吗?还是因为其他原因导致的怪癖?
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.bounds = CGRect(x: -100, y: -100, width: 200, height: 200)
view.backgroundColor = .white
let str = "..T.V.W.Y.."
let strStorage = NSTextStorage(string: str)
let layoutManager = NSLayoutManager()
strStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: view.bounds.size)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
let strArray = Array(str)
struct CharInfo {
var char: Character
var origin: CGPoint?
var size: CGSize?
}
var charInfoArray = [CharInfo]()
for index in 0..<str.count {
charInfoArray.append(CharInfo.init(char: strArray[index], origin: nil, size: nil))
let charRange = NSMakeRange(index, 1)
let charRect = layoutManager.boundingRect(forGlyphRange: charRange, in: textContainer)
charInfoArray[index].origin = charRect.origin
charInfoArray[index].size = charRect.size
}
for charInfo in charInfoArray {
let textView0 = UITextView()
textView0.backgroundColor = UIColor.clear // Ignored in this case!!
textView0.text = String(charInfo.char)
textView0.textContainerInset = UIEdgeInsets.zero
let size0 = charInfo.size!
textView0.frame = CGRect(origin: charInfo.origin!, size: size0)
textView0.textContainer.lineFragmentPadding = CGFloat(0.0)
textView0.textColor = UIColor.red
view.addSubview(textView0)
let textView1 = UITextView()
textView1.backgroundColor = UIColor.clear // Required
textView1.text = String(charInfo.char)
textView1.textContainerInset = UIEdgeInsets.zero
var size1 = charInfo.size!
size1.width = 20 // But changing .height has no effect on opacity
textView1.frame = CGRect(origin: charInfo.origin!, size: size1)
textView1.frame = textView1.frame.offsetBy(dx: 0, dy: 20)
textView1.textContainer.lineFragmentPadding = CGFloat(0.0)
textView1.textColor = UIColor.black
view.addSubview(textView1)
}
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
这看起来确实是一个错误,但它与 NSLayoutManager 的实例方法 boundingRect(forGlyphRange:in:) 有关。它只是看起来可能是透明度变化。
根据 Apple's documentation,boundingRect(forGlyphRange:in:) 应该是“[return] 一个单一的边界矩形(在容器坐标中)包含所有字形和在给定中绘制的其他标记给定字形范围的文本容器,包括绘制在其线段矩形之外的字形和下划线等文本属性。”但这不是它正在做的。
在这种情况下,由于字距调整,每个 boundingRect 的宽度减少了下一个字形向左移动的量。您可以对此进行测试,例如,使用 str = "ToT" 并在设置后立即添加 print(size0.width) 。你会得到这个:
6.0 // "T"; should have been 7.330078125
6.673828125 // "o"
7.330078125 // "T"
在修复此错误之前,解决方法是单独计算每个字符的字形大小。