| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- //
- // LNPostShareViewController.swift
- // Lanu
- //
- // Created by OneeChan on 2025/11/27.
- //
- import Foundation
- import UIKit
- import SnapKit
- import Combine
- extension UIView {
- func pushToPostShare() {
- let vc = LNPostShareViewController()
- navigationController?.pushViewController(vc, animated: true)
- }
- }
- class LNPostShareViewController: LNViewController {
- private let postView = UIImageView()
-
- private let skillStackView = UIStackView()
- private var selectedSkills: [LNGameMateSkillVO] = []
- private var lastDesc: String?
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- setupViews()
- prefetchSkillIcon()
- loadLastOrder()
-
- selectedSkills = Array(myUserInfo.skills.prefix(3))
- updateSkillsView()
- }
- }
- extension LNPostShareViewController {
- private func prefetchSkillIcon() {
- myUserInfo.skills.forEach {
- SDWebImageManager.shared.loadImage(with: URL(string: $0.icon),
- progress: nil)
- { _, _, _, _, _, _ in }
- }
- }
-
- private func loadLastOrder() {
- LNOrderManager.shared.getLastOrder { [weak self] res in
- guard let self else { return }
- guard let res else { return }
- lastDesc = .init(key: "A00220", Double(res.finishTime).formattedDate("."), res.nickname, res.bizCateGoryName)
- }
- }
- }
- extension LNPostShareViewController {
- private func updateSkillsView() {
- skillStackView.arrangedSubviews.forEach {
- skillStackView.removeArrangedSubview($0)
- $0.removeFromSuperview()
- }
-
- selectedSkills.prefix(3).forEach {
- let itemView = LNPostShareSkillItemView()
- itemView.icon.sd_setImage(with: URL(string: $0.icon))
- itemView.nameLabel.text = $0.name
- skillStackView.addArrangedSubview(itemView)
- }
- }
-
- private func showSkillSelectPanel() {
- guard !myUserInfo.skills.isEmpty else { return }
- let panel = LNPostSkillSelectPanel()
- panel.setSkills(skills: myUserInfo.skills, selecteds: selectedSkills)
- panel.handler = { [weak self] selections in
- guard let self else { return }
- selectedSkills = selections
- updateSkillsView()
- }
- panel.popup(view)
- }
-
- private func setupViews() {
- title = .init(key: "A00221")
- view.backgroundColor = .primary_1
-
- let topCover = UIImageView()
- topCover.image = .icHomeTopBg
- view.addSubview(topCover)
- topCover.snp.makeConstraints { make in
- make.top.leading.trailing.equalToSuperview()
- }
-
- let scrollView = UIScrollView()
- scrollView.backgroundColor = .clear
- scrollView.showsHorizontalScrollIndicator = false
- scrollView.showsVerticalScrollIndicator = false
- view.addSubview(scrollView)
- scrollView.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- let fakeView = UIView()
- scrollView.addSubview(fakeView)
- fakeView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview()
- make.width.equalToSuperview()
- make.height.equalTo(0)
- }
-
- let post = buildPost()
- scrollView.addSubview(post)
- post.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.top.equalToSuperview().offset(32)
- }
-
- let skill = buildSkillView()
- scrollView.addSubview(skill)
- skill.snp.makeConstraints { make in
- make.top.equalTo(post.snp.bottom)
- make.horizontalEdges.equalToSuperview().inset(16)
- make.height.equalTo(100)
- }
-
- let album = buildAlbum()
- scrollView.addSubview(album)
- album.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(16)
- make.top.equalTo(skill.snp.bottom).offset(20)
- make.trailing.equalToSuperview()
- make.bottom.equalToSuperview().offset(-view.safeBottomInset - 45)
- }
-
- let share = UIButton()
- share.setTitle(.init(key: "A00222"), for: .normal)
- share.setTitleColor(.text_1, for: .normal)
- share.setBackgroundImage(.primary_8, for: .normal)
- share.layer.cornerRadius = 23.5
- share.clipsToBounds = true
- share.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- guard let album = postView.image else { return }
-
- let generator = LNPostShareImageGenerator()
- generator.setOrderInfo(desc: lastDesc)
- generator.setAlbum(image: album)
- generator.setSkills(skills: selectedSkills)
- generator.setShareQRCode(url: .profileShareUrl + "?id=\(myUid)&share=app")
- let shareImage = generator.generate()
- shareImage.saveToLibrary { [weak self] success, err in
- guard let self else { return }
- if let err {
- showToast(err.localizedDescription)
- } else {
- showToast(.init(key: "A00157"))
- navigationController?.popViewController(animated: true)
- }
- }
- }), for: .touchUpInside)
- view.addSubview(share)
- share.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom).offset(-4)
- make.height.equalTo(47)
- }
- }
-
- private func buildPost() -> UIView {
- let container = UIView()
- container.backgroundColor = .fill
- container.layer.cornerRadius = 20
-
- 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 bio = UILabel()
- bio.font = .body_m
- bio.textColor = .text_4
- bio.numberOfLines = 0
- bio.text = myUserInfo.intro
- 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)
- }
-
- let line = buildDashLine()
- container.layer.addSublayer(line)
- container.publisher(for: \.bounds).removeDuplicates().sink
- { [weak line] newValue in
- guard let line else { return }
- line.frame = .init(
- x: 14,
- y: newValue.height - 1,
- width: newValue.width - 28,
- height: 1)
-
- let path = UIBezierPath()
- path.move(to: CGPoint(x: 0, y: 0))
- path.addLine(to: CGPoint(x: newValue.width - 28, y: 0))
- line.path = path.cgPath
- }.store(in: &cancellables)
-
- 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 = "\(myUserInfo.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 buildDashLine() -> CAShapeLayer {
- let lineLayer = CAShapeLayer()
- lineLayer.frame = .zero
- lineLayer.fillColor = UIColor.clear.cgColor
- lineLayer.strokeColor = UIColor.fill_3.cgColor
- lineLayer.lineWidth = 2.0
- lineLayer.lineDashPattern = [NSNumber(value: 3), NSNumber(value: 3)]
-
- return lineLayer
- }
-
- private func buildSkillView() -> UIView {
- let container = UIView()
- container.backgroundColor = .fill
- container.layer.cornerRadius = 20
- container.onTap { [weak self] in
- guard let self else { return }
- showSkillSelectPanel()
- }
-
- skillStackView.axis = .horizontal
- skillStackView.spacing = 10
- container.addSubview(skillStackView)
- skillStackView.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(113)
- make.bottom.equalToSuperview().offset(-18)
- }
-
- let ic = UIImageView()
- ic.image = .icHomeTabSelected
- 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: "A00219")
- 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)
- }
-
- let arrow = UIImageView.arrowImageView(size: 16)
- container.addSubview(arrow)
- arrow.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-8)
- }
-
- return container
- }
-
- private func buildAlbum() -> UIView {
- let scrollView = UIScrollView()
- scrollView.showsVerticalScrollIndicator = false
- scrollView.showsHorizontalScrollIndicator = false
-
- let fakeView = UIView()
- scrollView.addSubview(fakeView)
- fakeView.snp.makeConstraints { make in
- make.verticalEdges.equalToSuperview()
- make.height.equalToSuperview()
- make.leading.equalToSuperview()
- make.width.equalTo(0)
- }
-
- let stackView = UIStackView()
- stackView.axis = .horizontal
- stackView.spacing = 10
- scrollView.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- var albumItemViews: [LNSharePostAlbumItemView] = []
- myUserInfo.photos.forEach { url in
- let album = LNSharePostAlbumItemView()
- album.imageView.sd_setImage(with: URL(string: url))
- album.onTap { [weak self, weak album] in
- guard let self, let album else { return }
- albumItemViews.forEach {
- $0.isSelected = $0 == album
- }
- postView.sd_setImage(with: URL(string: url))
- }
- album.isSelected = false
- stackView.addArrangedSubview(album)
- albumItemViews.append(album)
- }
- albumItemViews.first?.isSelected = true
- postView.sd_setImage(with: URL(string: myUserInfo.photos.first ?? ""))
-
- return scrollView
- }
- }
- private class LNSharePostAlbumItemView: UIView {
- let imageView = UIImageView()
- var isSelected: Bool = false {
- didSet {
- if isSelected {
- imageView.layer.cornerRadius = 11
- imageView.snp.remakeConstraints { make in
- make.center.equalToSuperview()
- make.width.height.equalToSuperview().inset(2)
- }
- selectedBorder.isHidden = false
- alpha = 1.0
- } else {
- imageView.layer.cornerRadius = 12
- imageView.snp.remakeConstraints { make in
- make.center.equalToSuperview()
- make.width.height.equalToSuperview()
- }
- selectedBorder.isHidden = true
- alpha = 0.5
- }
- }
- }
- private let selectedBorder = UIImageView()
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- snp.makeConstraints { make in
- make.width.height.equalTo(90)
- }
-
- selectedBorder.image = .primary_7
- selectedBorder.layer.cornerRadius = 12
- selectedBorder.clipsToBounds = true
- selectedBorder.isHidden = true
- addSubview(selectedBorder)
- selectedBorder.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- imageView.clipsToBounds = true
- imageView.contentMode = .scaleAspectFill
- addSubview(imageView)
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- #if DEBUG
- import SwiftUI
- struct LNSharePostViewControllerPreview: UIViewControllerRepresentable {
- func makeUIViewController(context: Context) -> some UIViewController {
- LNPostShareViewController()
- }
-
- func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }
- }
- #Preview(body: {
- LNSharePostViewControllerPreview()
- })
- #endif
|