|
|
@@ -0,0 +1,319 @@
|
|
|
+//
|
|
|
+// LNPostShareImageGenerator.swift
|
|
|
+// Lanu
|
|
|
+//
|
|
|
+// Created by OneeChan on 2025/11/28.
|
|
|
+//
|
|
|
+
|
|
|
+import Foundation
|
|
|
+import UIKit
|
|
|
+import SnapKit
|
|
|
+
|
|
|
+
|
|
|
+class LNPostShareImageGenerator {
|
|
|
+ private let container = UIImageView()
|
|
|
+ private let postView = UIImageView()
|
|
|
+ private let qrCodeView = UIImageView()
|
|
|
+
|
|
|
+ init() {
|
|
|
+ setupViews()
|
|
|
+ }
|
|
|
+
|
|
|
+ func update(album: UIImage) {
|
|
|
+ postView.image = album
|
|
|
+
|
|
|
+ container.layoutIfNeeded()
|
|
|
+ }
|
|
|
+
|
|
|
+ func generate() -> UIImage {
|
|
|
+ let renderer = UIGraphicsImageRenderer(bounds: container.bounds)
|
|
|
+
|
|
|
+ return renderer.image { _ in
|
|
|
+ container.drawHierarchy(
|
|
|
+ in: container.bounds,
|
|
|
+ afterScreenUpdates: true
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ required init?(coder: NSCoder) {
|
|
|
+ fatalError("init(coder:) has not been implemented")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension LNPostShareImageGenerator {
|
|
|
+ private func setupViews() {
|
|
|
+ let image: UIImage = .init(named: "ic_post_share")!
|
|
|
+ container.image = image
|
|
|
+ container.frame = .init(x: 0, y: 0, width: image.size.width, height: image.size.height)
|
|
|
+
|
|
|
+ let record = buildRecord()
|
|
|
+ container.addSubview(record)
|
|
|
+ record.snp.makeConstraints { make in
|
|
|
+ make.leading.top.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let post = buildPost()
|
|
|
+ container.addSubview(post)
|
|
|
+ post.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(16)
|
|
|
+ make.top.equalToSuperview().offset(59)
|
|
|
+ }
|
|
|
+
|
|
|
+ let album = buildSkillView()
|
|
|
+ container.addSubview(album)
|
|
|
+ album.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(16)
|
|
|
+ make.bottom.equalToSuperview().offset(-152)
|
|
|
+ make.height.equalTo(100)
|
|
|
+ }
|
|
|
+
|
|
|
+ let url = "http://localhost:3000/user/category"
|
|
|
+ qrCodeView.image = url.toQRCode(size: 88)
|
|
|
+ container.addSubview(qrCodeView)
|
|
|
+ qrCodeView.snp.makeConstraints { make in
|
|
|
+ make.trailing.equalToSuperview().offset(-74)
|
|
|
+ make.bottom.equalToSuperview().offset(-33)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildRecord() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.snp.makeConstraints { make in
|
|
|
+ make.height.equalTo(32)
|
|
|
+ }
|
|
|
+
|
|
|
+ let gradientLayer = CAGradientLayer()
|
|
|
+ gradientLayer.frame = CGRect(origin: .zero, size: .init(width: self.container.bounds.size.width, height: 32))
|
|
|
+ gradientLayer.colors = [UIColor.init(hex: "#E0FFED").cgColor, UIColor.init(hex: "#FFFFFF00").cgColor]
|
|
|
+ gradientLayer.locations = [0, 1]
|
|
|
+ gradientLayer.startPoint = .init(x: 0, y: 0)
|
|
|
+ gradientLayer.endPoint = .init(x: 1, y: 0)
|
|
|
+ container.layer.insertSublayer(gradientLayer, at: 0)
|
|
|
+
|
|
|
+ let ic = UIImageView()
|
|
|
+ ic.image = .init(named: "ic_post_share_new")
|
|
|
+ container.addSubview(ic)
|
|
|
+ ic.snp.makeConstraints { make in
|
|
|
+ make.leading.equalToSuperview().offset(13)
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let recordLabel = UILabel()
|
|
|
+ recordLabel.font = .body_xs
|
|
|
+ recordLabel.textColor = .init(hex: "#466811")
|
|
|
+ recordLabel.text = .init(key: "在 11.11 与「用户xxx」进行了一场「网球」比赛")
|
|
|
+ container.addSubview(recordLabel)
|
|
|
+ recordLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalTo(ic.snp.trailing).offset(6)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildPost() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+
|
|
|
+ let info = buildUserInfo()
|
|
|
+ container.addSubview(info)
|
|
|
+ info.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(16)
|
|
|
+ make.top.equalToSuperview().offset(12)
|
|
|
+ }
|
|
|
+
|
|
|
+ postView.contentMode = .scaleAspectFill
|
|
|
+ postView.layer.cornerRadius = 20
|
|
|
+ postView.clipsToBounds = true
|
|
|
+ container.addSubview(postView)
|
|
|
+ postView.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(16)
|
|
|
+ make.top.equalToSuperview().offset(44)
|
|
|
+ make.height.equalTo(postView.snp.width).multipliedBy(317.0/311.0)
|
|
|
+ }
|
|
|
+
|
|
|
+ let tag = UIImageView()
|
|
|
+ tag.image = .init(named: "ic_post_location_bg")
|
|
|
+ container.addSubview(tag)
|
|
|
+ tag.snp.makeConstraints { make in
|
|
|
+ make.leading.bottom.equalTo(postView)
|
|
|
+ }
|
|
|
+
|
|
|
+ let locationIc = UIImageView()
|
|
|
+ locationIc.image = .init(named: "ic_location")
|
|
|
+ tag.addSubview(locationIc)
|
|
|
+ locationIc.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalToSuperview().offset(13)
|
|
|
+ make.width.height.equalTo(16)
|
|
|
+ }
|
|
|
+
|
|
|
+ let locationLabel = UILabel()
|
|
|
+ locationLabel.text = myGameMateInfo.area
|
|
|
+ locationLabel.font = .body_m
|
|
|
+ locationLabel.textColor = .text_4
|
|
|
+ tag.addSubview(locationLabel)
|
|
|
+ locationLabel.snp.makeConstraints { make in
|
|
|
+ make.leading.equalTo(locationIc.snp.trailing).offset(4)
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.lessThanOrEqualToSuperview().offset(-13)
|
|
|
+ }
|
|
|
+
|
|
|
+ let bio = UILabel()
|
|
|
+ bio.text = myUserInfo.intro
|
|
|
+ bio.font = .body_m
|
|
|
+ bio.textColor = .text_4
|
|
|
+ container.addSubview(bio)
|
|
|
+ bio.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(22)
|
|
|
+ make.top.equalTo(postView.snp.bottom).offset(14)
|
|
|
+ make.bottom.equalToSuperview().offset(-20)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildUserInfo() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+ container.snp.makeConstraints { make in
|
|
|
+ make.height.equalTo(22)
|
|
|
+ }
|
|
|
+
|
|
|
+ let userNameLabel = UILabel()
|
|
|
+ userNameLabel.text = myUserInfo.nickname
|
|
|
+ userNameLabel.font = .heading_h2
|
|
|
+ userNameLabel.textColor = .text_5
|
|
|
+ container.addSubview(userNameLabel)
|
|
|
+ userNameLabel.snp.makeConstraints { make in
|
|
|
+ make.leading.centerY.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let gender = LNGenderView()
|
|
|
+ gender.update(myUserInfo.gender, myUserInfo.age)
|
|
|
+ container.addSubview(gender)
|
|
|
+ gender.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalTo(userNameLabel.snp.trailing).offset(4)
|
|
|
+ }
|
|
|
+
|
|
|
+ let starLabel = UILabel()
|
|
|
+ starLabel.text = "\(myGameMateInfo.star)"
|
|
|
+ starLabel.font = .heading_h2
|
|
|
+ starLabel.textColor = .text_5
|
|
|
+ container.addSubview(starLabel)
|
|
|
+ starLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ let star = LNStarScoreView()
|
|
|
+ star.score = 1.0
|
|
|
+ star.icSize = 18
|
|
|
+ container.addSubview(star)
|
|
|
+ star.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.trailing.equalTo(starLabel.snp.leading).offset(-4)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildSkillView() -> UIView {
|
|
|
+ let container = UIView()
|
|
|
+
|
|
|
+ let stackView = UIStackView()
|
|
|
+ stackView.axis = .horizontal
|
|
|
+ stackView.spacing = 10
|
|
|
+ container.addSubview(stackView)
|
|
|
+ stackView.snp.makeConstraints { make in
|
|
|
+ make.leading.equalToSuperview().offset(113)
|
|
|
+ make.bottom.equalToSuperview().offset(-18)
|
|
|
+ }
|
|
|
+
|
|
|
+ myGameMateInfo.skills.prefix(3).forEach {
|
|
|
+ let itemView = LNPostShareSkillItemView()
|
|
|
+ itemView.icon.sd_setImage(with: URL(string: $0.icon))
|
|
|
+ itemView.nameLabel.text = $0.name
|
|
|
+ stackView.addArrangedSubview(itemView)
|
|
|
+ }
|
|
|
+
|
|
|
+ let ic = UIImageView()
|
|
|
+ ic.image = .init(named: "ic_main_tab_selected")
|
|
|
+ ic.alpha = 0.5
|
|
|
+ container.addSubview(ic)
|
|
|
+ ic.snp.makeConstraints { make in
|
|
|
+ make.leading.equalToSuperview().offset(20)
|
|
|
+ make.top.equalToSuperview().offset(31)
|
|
|
+ }
|
|
|
+
|
|
|
+ let tipsLabel = UILabel()
|
|
|
+ tipsLabel.text = .init(key: "扫码跟我一起玩")
|
|
|
+ tipsLabel.font = .heading_h4
|
|
|
+ tipsLabel.textColor = .text_5
|
|
|
+ tipsLabel.numberOfLines = 0
|
|
|
+ container.addSubview(tipsLabel)
|
|
|
+ tipsLabel.snp.makeConstraints { make in
|
|
|
+ make.centerY.equalToSuperview()
|
|
|
+ make.leading.equalToSuperview().offset(30)
|
|
|
+ make.width.equalTo(60)
|
|
|
+ }
|
|
|
+
|
|
|
+ return container
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private class LNPostShareSkillItemView: UIView {
|
|
|
+ let icon = UIImageView()
|
|
|
+ let nameLabel = UILabel()
|
|
|
+
|
|
|
+ override init(frame: CGRect) {
|
|
|
+ super.init(frame: frame)
|
|
|
+
|
|
|
+ let bg = UIImageView()
|
|
|
+ bg.image = .primary_7
|
|
|
+ bg.layer.cornerRadius = 26
|
|
|
+ bg.clipsToBounds = true
|
|
|
+ addSubview(bg)
|
|
|
+ bg.snp.makeConstraints { make in
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ make.top.equalToSuperview()
|
|
|
+ make.width.height.equalTo(52)
|
|
|
+ }
|
|
|
+
|
|
|
+ icon.backgroundColor = .fill
|
|
|
+ icon.layer.cornerRadius = 25.5
|
|
|
+ icon.clipsToBounds = true
|
|
|
+ addSubview(icon)
|
|
|
+ icon.snp.makeConstraints { make in
|
|
|
+ make.center.equalTo(bg)
|
|
|
+ make.width.height.equalTo(bg).inset(1)
|
|
|
+ }
|
|
|
+
|
|
|
+ let nameBg = UIImageView()
|
|
|
+ nameBg.image = .primary_7
|
|
|
+ nameBg.layer.cornerRadius = 10.5
|
|
|
+ nameBg.clipsToBounds = true
|
|
|
+ addSubview(nameBg)
|
|
|
+ nameBg.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview()
|
|
|
+ make.bottom.equalToSuperview()
|
|
|
+ make.top.equalTo(bg.snp.bottom).offset(-9)
|
|
|
+ make.height.equalTo(21)
|
|
|
+ make.width.equalTo(67)
|
|
|
+ }
|
|
|
+
|
|
|
+ nameLabel.font = .body_xs
|
|
|
+ nameLabel.textColor = .text_1
|
|
|
+ nameLabel.textAlignment = .center
|
|
|
+ nameBg.addSubview(nameLabel)
|
|
|
+ nameLabel.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ make.horizontalEdges.equalToSuperview().inset(6)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ required init?(coder: NSCoder) {
|
|
|
+ fatalError("init(coder:) has not been implemented")
|
|
|
+ }
|
|
|
+}
|