diff --git a/MobileFort/MobileFort/Common.swift b/MobileFort/MobileFort/Common.swift index 028f855..8ad3ef2 100644 --- a/MobileFort/MobileFort/Common.swift +++ b/MobileFort/MobileFort/Common.swift @@ -56,7 +56,7 @@ struct Post: Decodable, Identifiable { struct ParsedPostContainer { let post: Post - let contentAttributed: NSAttributedString + let contentAttributed: NSMutableAttributedString } let mediaURL = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png" diff --git a/MobileFort/MobileFort/PostView.swift b/MobileFort/MobileFort/PostView.swift index 59cc806..c117cde 100644 --- a/MobileFort/MobileFort/PostView.swift +++ b/MobileFort/MobileFort/PostView.swift @@ -8,9 +8,9 @@ extension String { } final class AttributedTextComponent: UIViewRepresentable { - let string: NSAttributedString + let string: NSMutableAttributedString - init(_ string: NSAttributedString) { + init(_ string: NSMutableAttributedString) { self.string = string } @@ -27,30 +27,56 @@ final class AttributedTextComponent: UIViewRepresentable { } } +struct SizeKey: PreferenceKey { + static var defaultValue: CGSize = .zero + + static func reduce(value: inout CGSize, nextValue: () -> CGSize) { + value = nextValue() + } +} + struct AttributedText: View { - let html: NSAttributedString + let html: NSMutableAttributedString let component: AttributedTextComponent @State var height: CGFloat = 0.0 - - init(_ html: NSAttributedString) { + @State var lastSize: CGSize = .zero + + init(_ html: NSMutableAttributedString) { self.html = html self.component = AttributedTextComponent(html) } + + func calculateHeight(size: CGSize) { + let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: .greatestFiniteMagnitude)) + + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + + label.attributedText = self.html + + label.sizeToFit() + + self.height = label.frame.height + self.lastSize = size + } var body: some View { - component.onAppear { - let label = UILabel() + ZStack { + GeometryReader { geometry in + Rectangle().fill(Color.clear).preference(key: SizeKey.self, value: geometry.size) + }.onPreferenceChange(SizeKey.self, perform: { size in + self.calculateHeight(size: size) + }) - label.numberOfLines = 0 - label.lineBreakMode = .byWordWrapping - label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - - label.attributedText = self.html - label.sizeToFit() - - self.height = label.frame.height + 15.0 // for padding - }.frame(minWidth: 0.0, maxWidth: .infinity, minHeight: 0.0, maxHeight: height) + component + } + .frame(minWidth: 0.0, maxWidth: .infinity, minHeight: self.height, maxHeight: self.height) + .onAppear { + if self.lastSize != .zero { + self.calculateHeight(size: self.lastSize) + } + } } } @@ -108,6 +134,6 @@ struct PostView: View { struct PostView_Previews: PreviewProvider { static var previews: some View { - return PostView(post: ParsedPostContainer(post: fooPost, contentAttributed: NSAttributedString())) + return PostView(post: ParsedPostContainer(post: fooPost, contentAttributed: NSMutableAttributedString())) } } diff --git a/MobileFort/MobileFort/ProfileView.swift b/MobileFort/MobileFort/ProfileView.swift index 0b4295f..135f616 100644 --- a/MobileFort/MobileFort/ProfileView.swift +++ b/MobileFort/MobileFort/ProfileView.swift @@ -1,5 +1,32 @@ import SwiftUI +extension NSMutableAttributedString { + func with(font: UIFont) -> NSMutableAttributedString { + enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, length), options: .longestEffectiveRangeNotRequired, using: { (value, range, stop) in + if let originalFont = value as? UIFont, let newFont = applyTraitsFromFont(originalFont, to: font) { + addAttribute(NSAttributedString.Key.font, value: newFont, range: range) + } + }) + + return self + } + + func applyTraitsFromFont(_ originalFont: UIFont, to newFont: UIFont) -> UIFont? { + let originalTrait = originalFont.fontDescriptor.symbolicTraits + + if originalTrait.contains(.traitBold) { + var traits = newFont.fontDescriptor.symbolicTraits + traits.insert(.traitBold) + + if let fontDescriptor = newFont.fontDescriptor.withSymbolicTraits(traits) { + return UIFont.init(descriptor: fontDescriptor, size: 0) + } + } + + return newFont + } +} + struct ProfileView: View { let username: String @@ -28,7 +55,7 @@ struct ProfileView: View { var postArray = [ParsedPostContainer]() for post in decodedPosts.posts { - let container = ParsedPostContainer(post: post, contentAttributed: (try? NSAttributedString(data: post.getContent().data(using: .utf8)!, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil))!) + let container = ParsedPostContainer(post: post, contentAttributed: (try? NSMutableAttributedString(data: post.getContent().data(using: .utf8)!, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil))!.with(font: UIFont.preferredFont(forTextStyle: .body))) postArray.append(container) }