LNPostShareImageGenerator.swift 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. //
  2. // LNPostShareImageGenerator.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/11/28.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNPostShareImageGenerator {
  11. fileprivate let container = UIImageView()
  12. private let newsView = UIView()
  13. private let newsLabel = UILabel()
  14. private let nameLabel = UILabel()
  15. private let genderView = LNGenderView()
  16. private let starLabel = UILabel()
  17. private let postView = UIImageView()
  18. private let bioLabel = UILabel()
  19. private let skillsView = UIStackView()
  20. private let qrCodeView = UIImageView()
  21. init() {
  22. setupViews()
  23. }
  24. func setInfo() {
  25. nameLabel.text = myUserInfo.nickname
  26. genderView.update(myUserInfo.gender, myUserInfo.age)
  27. starLabel.text = "\(myUserInfo.star)"
  28. bioLabel.text = myUserInfo.intro
  29. }
  30. func setAlbum(image: UIImage) {
  31. postView.image = image
  32. }
  33. func setOrderInfo(desc: String?) {
  34. newsLabel.text = desc?.isEmpty == false ? desc : .init(key: "A00218")
  35. }
  36. func setSkills(skills: [LNGameMateSkillVO]) {
  37. skillsView.arrangedSubviews.forEach {
  38. skillsView.removeArrangedSubview($0)
  39. $0.removeFromSuperview()
  40. }
  41. skills.prefix(3).forEach {
  42. let itemView = LNPostShareSkillItemView()
  43. itemView.icon.sd_setImage(with: URL(string: $0.icon))
  44. itemView.nameLabel.text = $0.name
  45. skillsView.addArrangedSubview(itemView)
  46. }
  47. }
  48. func setShareQRCode(url: String) {
  49. qrCodeView.image = url.toQRCode(size: 88)
  50. }
  51. func generate() -> UIImage {
  52. let renderer = UIGraphicsImageRenderer(bounds: container.bounds)
  53. return renderer.image { _ in
  54. container.drawHierarchy(
  55. in: container.bounds,
  56. afterScreenUpdates: true
  57. )
  58. }
  59. }
  60. }
  61. extension LNPostShareImageGenerator {
  62. private func setupViews() {
  63. let image: UIImage = .icPostShare
  64. container.image = image
  65. container.frame = .init(x: 0, y: 0, width: image.size.width, height: image.size.height)
  66. let record = buildRecord()
  67. container.addSubview(record)
  68. record.snp.makeConstraints { make in
  69. make.leading.top.equalToSuperview()
  70. make.trailing.equalToSuperview()
  71. }
  72. let post = buildPost()
  73. container.addSubview(post)
  74. post.snp.makeConstraints { make in
  75. make.horizontalEdges.equalToSuperview().inset(16)
  76. make.top.equalToSuperview().offset(59)
  77. }
  78. let album = buildSkillView()
  79. container.addSubview(album)
  80. album.snp.makeConstraints { make in
  81. make.horizontalEdges.equalToSuperview().inset(16)
  82. make.bottom.equalToSuperview().offset(-152)
  83. make.height.equalTo(100)
  84. }
  85. container.addSubview(qrCodeView)
  86. qrCodeView.snp.makeConstraints { make in
  87. make.trailing.equalToSuperview().offset(-74)
  88. make.bottom.equalToSuperview().offset(-33)
  89. }
  90. }
  91. private func buildRecord() -> UIView {
  92. newsView.snp.makeConstraints { make in
  93. make.height.equalTo(32)
  94. }
  95. let gradientLayer = CAGradientLayer()
  96. gradientLayer.frame = CGRect(origin: .zero, size: .init(width: self.container.bounds.size.width, height: 32))
  97. gradientLayer.colors = [UIColor.init(hex: "#E0FFED").cgColor, UIColor.init(hex: "#FFFFFF00").cgColor]
  98. gradientLayer.locations = [0, 1]
  99. gradientLayer.startPoint = .init(x: 0, y: 0)
  100. gradientLayer.endPoint = .init(x: 1, y: 0)
  101. newsView.layer.insertSublayer(gradientLayer, at: 0)
  102. let ic = UIImageView()
  103. ic.image = .icPostShareNew
  104. newsView.addSubview(ic)
  105. ic.snp.makeConstraints { make in
  106. make.leading.equalToSuperview().offset(13)
  107. make.centerY.equalToSuperview()
  108. }
  109. newsLabel.font = .body_xs
  110. newsLabel.textColor = .init(hex: "#466811")
  111. newsView.addSubview(newsLabel)
  112. newsLabel.snp.makeConstraints { make in
  113. make.centerY.equalToSuperview()
  114. make.leading.equalTo(ic.snp.trailing).offset(6)
  115. }
  116. return newsView
  117. }
  118. private func buildPost() -> UIView {
  119. let container = UIView()
  120. let info = buildUserInfo()
  121. container.addSubview(info)
  122. info.snp.makeConstraints { make in
  123. make.horizontalEdges.equalToSuperview().inset(16)
  124. make.top.equalToSuperview().offset(12)
  125. }
  126. postView.contentMode = .scaleAspectFill
  127. postView.layer.cornerRadius = 20
  128. postView.clipsToBounds = true
  129. container.addSubview(postView)
  130. postView.snp.makeConstraints { make in
  131. make.horizontalEdges.equalToSuperview().inset(16)
  132. make.top.equalToSuperview().offset(44)
  133. make.height.equalTo(postView.snp.width).multipliedBy(317.0/311.0)
  134. }
  135. bioLabel.font = .body_m
  136. bioLabel.textColor = .text_4
  137. bioLabel.numberOfLines = 3
  138. container.addSubview(bioLabel)
  139. bioLabel.snp.makeConstraints { make in
  140. make.horizontalEdges.equalToSuperview().inset(22)
  141. make.top.equalTo(postView.snp.bottom).offset(14)
  142. make.bottom.equalToSuperview().offset(-20)
  143. }
  144. return container
  145. }
  146. private func buildUserInfo() -> UIView {
  147. let container = UIView()
  148. container.snp.makeConstraints { make in
  149. make.height.equalTo(22)
  150. }
  151. nameLabel.font = .heading_h2
  152. nameLabel.textColor = .text_5
  153. container.addSubview(nameLabel)
  154. nameLabel.snp.makeConstraints { make in
  155. make.leading.centerY.equalToSuperview()
  156. }
  157. container.addSubview(genderView)
  158. genderView.snp.makeConstraints { make in
  159. make.centerY.equalToSuperview()
  160. make.leading.equalTo(nameLabel.snp.trailing).offset(4)
  161. }
  162. starLabel.font = .heading_h2
  163. starLabel.textColor = .text_5
  164. container.addSubview(starLabel)
  165. starLabel.snp.makeConstraints { make in
  166. make.centerY.equalToSuperview()
  167. make.trailing.equalToSuperview()
  168. }
  169. let star = LNStarScoreView()
  170. star.score = 1.0
  171. star.icSize = 18
  172. container.addSubview(star)
  173. star.snp.makeConstraints { make in
  174. make.centerY.equalToSuperview()
  175. make.trailing.equalTo(starLabel.snp.leading).offset(-4)
  176. }
  177. return container
  178. }
  179. private func buildSkillView() -> UIView {
  180. let container = UIView()
  181. skillsView.axis = .horizontal
  182. skillsView.spacing = 10
  183. container.addSubview(skillsView)
  184. skillsView.snp.makeConstraints { make in
  185. make.leading.equalToSuperview().offset(113)
  186. make.bottom.equalToSuperview().offset(-18)
  187. }
  188. let ic = UIImageView()
  189. ic.image = .icHomeTabSelected
  190. ic.alpha = 0.5
  191. container.addSubview(ic)
  192. ic.snp.makeConstraints { make in
  193. make.leading.equalToSuperview().offset(20)
  194. make.top.equalToSuperview().offset(31)
  195. }
  196. let tipsLabel = UILabel()
  197. tipsLabel.text = .init(key: "A00219")
  198. tipsLabel.font = .heading_h4
  199. tipsLabel.textColor = .text_5
  200. tipsLabel.numberOfLines = 0
  201. container.addSubview(tipsLabel)
  202. tipsLabel.snp.makeConstraints { make in
  203. make.centerY.equalToSuperview()
  204. make.leading.equalToSuperview().offset(30)
  205. make.width.equalTo(60)
  206. }
  207. return container
  208. }
  209. }
  210. #if DEBUG
  211. import SwiftUI
  212. struct LNPostShareImagePreview: UIViewRepresentable {
  213. func makeUIView(context: Context) -> some UIView {
  214. let container = UIView()
  215. container.backgroundColor = .lightGray
  216. let generator = LNPostShareImageGenerator()
  217. container.addSubview(generator.container)
  218. generator.container.snp.makeConstraints { make in
  219. make.center.equalToSuperview()
  220. }
  221. return container
  222. }
  223. func updateUIView(_ uiView: UIViewType, context: Context) { }
  224. }
  225. #Preview(body: {
  226. LNPostShareImagePreview()
  227. })
  228. #endif // DEBUG