diff --git a/MobileFort/MobileFort.xcodeproj/project.pbxproj b/MobileFort/MobileFort.xcodeproj/project.pbxproj index db5cc6d..bfe01e9 100644 --- a/MobileFort/MobileFort.xcodeproj/project.pbxproj +++ b/MobileFort/MobileFort.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 03BCD7432488947200DA1F27 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BCD7422488947200DA1F27 /* ProfileView.swift */; }; 03BCD7452488948200DA1F27 /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BCD7442488948200DA1F27 /* PostView.swift */; }; 03BCD7482488985A00DA1F27 /* RemoteImage in Frameworks */ = {isa = PBXBuildFile; productRef = 03BCD7472488985A00DA1F27 /* RemoteImage */; }; + 03BCD74A2489322500DA1F27 /* AttributedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BCD7492489322500DA1F27 /* AttributedText.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -29,6 +30,7 @@ 03427F6C248887D200A0073D /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = ""; }; 03BCD7422488947200DA1F27 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; 03BCD7442488948200DA1F27 /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = ""; }; + 03BCD7492489322500DA1F27 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -71,6 +73,7 @@ 03427F6C248887D200A0073D /* Common.swift */, 03BCD7422488947200DA1F27 /* ProfileView.swift */, 03BCD7442488948200DA1F27 /* PostView.swift */, + 03BCD7492489322500DA1F27 /* AttributedText.swift */, ); path = MobileFort; sourceTree = ""; @@ -155,6 +158,7 @@ 03BCD7432488947200DA1F27 /* ProfileView.swift in Sources */, 03427F5B2488856C00A0073D /* SceneDelegate.swift in Sources */, 03BCD7452488948200DA1F27 /* PostView.swift in Sources */, + 03BCD74A2489322500DA1F27 /* AttributedText.swift in Sources */, 03427F5D2488856C00A0073D /* MainView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/MobileFort/MobileFort/AttributedText.swift b/MobileFort/MobileFort/AttributedText.swift new file mode 100644 index 0000000..0065bf4 --- /dev/null +++ b/MobileFort/MobileFort/AttributedText.swift @@ -0,0 +1,101 @@ +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 + } +} + +final class AttributedTextComponent: UIViewRepresentable { + let string: NSMutableAttributedString + + init(_ string: NSMutableAttributedString) { + self.string = string + } + + public func makeUIView(context: UIViewRepresentableContext) -> UILabel { + UILabel() + } + + func updateUIView(_ uiView: UILabel, context: Context) { + uiView.backgroundColor = .clear + uiView.numberOfLines = 0 + uiView.lineBreakMode = .byWordWrapping + uiView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + uiView.attributedText = string + } +} + +struct SizeKey: PreferenceKey { + static var defaultValue: CGSize = .zero + + static func reduce(value: inout CGSize, nextValue: () -> CGSize) { + value = nextValue() + } +} + +struct AttributedText: View { + let html: NSMutableAttributedString + let component: AttributedTextComponent + + @State var height: CGFloat = 0.0 + @State var lastSize: CGSize = .zero + + init(_ html: NSMutableAttributedString) { + self.html = html.with(font: UIFont.preferredFont(forTextStyle: .body)) + 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 { + 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) + }) + + component + } + .frame(minWidth: 0.0, maxWidth: .infinity, minHeight: self.height, maxHeight: self.height) + .onAppear { + if self.lastSize != .zero { + self.calculateHeight(size: self.lastSize) + } + } + } +} diff --git a/MobileFort/MobileFort/PostView.swift b/MobileFort/MobileFort/PostView.swift index c117cde..776d6f8 100644 --- a/MobileFort/MobileFort/PostView.swift +++ b/MobileFort/MobileFort/PostView.swift @@ -7,79 +7,6 @@ extension String { } } -final class AttributedTextComponent: UIViewRepresentable { - let string: NSMutableAttributedString - - init(_ string: NSMutableAttributedString) { - self.string = string - } - - public func makeUIView(context: UIViewRepresentableContext) -> UILabel { - UILabel() - } - - func updateUIView(_ uiView: UILabel, context: Context) { - uiView.backgroundColor = .clear - uiView.numberOfLines = 0 - uiView.lineBreakMode = .byWordWrapping - uiView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - uiView.attributedText = string - } -} - -struct SizeKey: PreferenceKey { - static var defaultValue: CGSize = .zero - - static func reduce(value: inout CGSize, nextValue: () -> CGSize) { - value = nextValue() - } -} - -struct AttributedText: View { - let html: NSMutableAttributedString - let component: AttributedTextComponent - - @State var height: CGFloat = 0.0 - @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 { - 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) - }) - - component - } - .frame(minWidth: 0.0, maxWidth: .infinity, minHeight: self.height, maxHeight: self.height) - .onAppear { - if self.lastSize != .zero { - self.calculateHeight(size: self.lastSize) - } - } - } -} - struct PostView: View { let post: ParsedPostContainer diff --git a/MobileFort/MobileFort/ProfileView.swift b/MobileFort/MobileFort/ProfileView.swift index 135f616..2801585 100644 --- a/MobileFort/MobileFort/ProfileView.swift +++ b/MobileFort/MobileFort/ProfileView.swift @@ -1,32 +1,5 @@ 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 @@ -55,7 +28,7 @@ struct ProfileView: View { var postArray = [ParsedPostContainer]() for post in decodedPosts.posts { - 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))) + let container = ParsedPostContainer(post: post, contentAttributed: (try? NSMutableAttributedString(data: post.getContent().data(using: .utf8)!, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil))!) postArray.append(container) }