I got a long string, let's call it Story
.
I fetch this story from a database so I don't know how long it is.
I want to display this story in a view, but what if the story is too long so it doesn't fit in one view?
I don't want to adjust the font size because it may be a very long story and making the font size smaller is not a good solution.
Therefore I want to split the Story into more than one view.
By passing the story and getting a separated story as an array of String
every item in the array can fit in one view.
This is the code, it hopefully gives you a hint what I'm trying to achieve exactly:
extension String {
/// - returns: The height that will fit Self
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.height)
}
#warning("it's so slow that i can't handle it in main thread , ( 21 second for 15 items in array )")
/// complition contains the separated string as array of string
func splitToFitSize(_ size : CGSize = UIScreen.main.bounds.size ,font : UIFont = UIFont.systemFont(ofSize: 17) , complition : @escaping (([String]) -> Void) ) {
DispatchQueue.global(qos: .background).async {
// contents contains all the words as Array of String
var contents = self.components(separatedBy: .whitespaces)
var values : [String] = []
for content in contents {
// if one word can't fit the size -> remove it , which it's not good, but i don't know what to do with it
guard content.isContentFit(size: size , font : font) else {contents.removeFirst(); continue;}
if values.count > 0 {
for (i , value) in values.enumerated() {
var newValue = value
newValue += " \(content)"
if newValue.isContentFit(size: size, font: font) {
values[i] = newValue
contents.removeFirst()
break;
}else if i == values.count - 1 {
values.append(content)
contents.removeFirst()
break;
}
}
}else {
values.append(content)
contents.removeFirst()
}
}
complition(values)
}
}
/// - returns: if Self can fit the passing size
private func isContentFit(size : CGSize, font : UIFont) -> Bool{
return self.height(withConstrainedWidth: size.width, font: font) < size.height
}
}
This code is working, but it takes very long. If I want to split a story to 15 views it takes 20 seconds or even longer.
How can I optimize this so it runs faster?