Files
youle_app_ios_v2/ylgamehall/Source/Share/SharePanel.swift
T
joywayer 9bd5a81e6c 4.B 分享逻辑对齐 daoqi msext:调用即触发 sharesuccess
【契约影响】docs/H5-Native-Contract.md §3.1[2]/§3.2[11]

H5 端 sharesuccess 时机改为"调用 friendsShare handler 即立刻发",与
原 msext 行为等价(live path 完全不等 SDK / URL Scheme 回包)。用户
明示有意为之:URL Scheme + 剪贴板路径本就没有可靠回包,乐观策略避免
H5 业务卡住。

3 平台分支严格 1:1 对齐 daoqi msext:

WechatShare(对齐 WechatShareManager shareWithContent)
  - type=="2" → 截图 JPEG 0.6 + sendImageData + app icon thumb
  - else      → 链接分享 sendLinkURL + app icon thumb
  - 移除原"远端图"分支(原项目无此分支)
  - 新增 appIconImage helper(读 Info.plist CFBundleIcons,对齐
    [self getAppIconImage])

QQShare(对齐 QQShareManager shareWithContent + simpleShareToQQFriend)
  - type=="2" → 截图 → UIPasteboard.image → 4 URL Scheme fallback
  - else      → mqqapi://share/to_{fri,qzone}?req_type=1&url=...
                &title=...&description=...(webpageUrl 空时 req_type=0)
  - 不依赖 QQ SDK(CLAUDE.md ADR-006)

DouyinShare(对齐 DouyinShareManager shareWithContent)
  - type=="2" → 截图 → UIPasteboard.image → "立即打开抖音"引导框
  - else      → title+"\n\n"+desc → UIPasteboard.string → 引导框
                双侧空时兜底 "来自进贤聚友棋牌的精彩内容分享"
  - 放弃之前的"存相册 + 自动拉起"激进路径,回到原项目"用户主动点"路径

架构调整:
- SharePlatform.share 改 sync fire-and-forget(去 ShareResult 返回)
- ShareCenter.dispatch / SharePanel.show 去 completion 参数
- FriendsShareHandler:解析后先发 responseCallback("sharefriend")
  与 sharesuccess({success:"2", type:<sharefriend>}),再异步 dispatch
2026-06-24 07:34:03 +08:00

161 lines
5.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// SharePanel.swift
// ylgamehall
//
// 原生分享面板:底部弹出三选一(微信好友 / QQ / 抖音)。
//
// 与 msext SharePanel.m 等价行为:
// - 半透明黑底覆盖全屏,点击空白区关闭
// - 内容视图从屏幕底部滑入,0.25s 动画
// - 三个圆形按钮 + label,等间距居中布局
// - 点击按钮后先 dismiss 再触发对应 SharePlatform.share
//
// Phase 4.B 阶段:UI 完整 + 调用 SharePlatform stub
// Phase 4.C/4.D 阶段:SharePlatform 升级为真实 URL Scheme 实现,本类不变。
//
import UIKit
import os.log
@MainActor
public final class SharePanel: UIView {
private static let log = Logger(subsystem: "ylgamehall", category: "SharePanel")
private static let panelHeight: CGFloat = 200
private static let buttonSize: CGFloat = 60
private static let animationDuration: TimeInterval = 0.25
private let content: ShareContent
private let contentView = UIView()
public init(content: ShareContent) {
self.content = content
super.init(frame: UIScreen.main.bounds)
backgroundColor = UIColor(white: 0, alpha: 0)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported")
}
// MARK: - 公共入口
/// 弹出分享面板。fire-and-forgetsharesuccess 已在 FriendsShareHandler 立即触发,
/// 用户取消(点空白)或选好平台后不再向 H5 回报结果。
public static func show(content: ShareContent) {
guard let window = topWindow() else { return }
let panel = SharePanel(content: content)
window.addSubview(panel)
panel.animateIn()
}
private static func topWindow() -> UIWindow? {
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap(\.windows)
.first { $0.isKeyWindow }
}
// MARK: - UI 装配
private func setupUI() {
// 半透明背景手势
let tap = UITapGestureRecognizer(target: self, action: #selector(handleBackgroundTap(_:)))
addGestureRecognizer(tap)
// 内容视图(屏幕底部,初始 Y 在屏幕外)
let screen = UIScreen.main.bounds
contentView.frame = CGRect(x: 0, y: screen.height, width: screen.width, height: Self.panelHeight)
contentView.backgroundColor = .white
let mask = CAShapeLayer()
mask.path = UIBezierPath(
roundedRect: contentView.bounds,
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: 10, height: 10)
).cgPath
contentView.layer.mask = mask
addSubview(contentView)
// 三个按钮 + label 等间距居中布局
let buttonsContainer = UIView(frame: CGRect(x: 0, y: 30, width: screen.width, height: 110))
contentView.addSubview(buttonsContainer)
let buttons: [(title: String, platform: SharePlatform, imageName: String)] = [
("微信", WechatShare.shared, "shareWechat"),
("QQ", QQShare.shared, "shareQQ"),
("抖音", DouyinShare.shared, "shareDouyin")
]
let totalButtonsWidth = Self.buttonSize * CGFloat(buttons.count)
let spacing = (screen.width - totalButtonsWidth) / CGFloat(buttons.count + 1)
for (index, b) in buttons.enumerated() {
let x = spacing + CGFloat(index) * (Self.buttonSize + spacing)
let button = UIButton(type: .custom)
button.frame = CGRect(x: x, y: 0, width: Self.buttonSize, height: Self.buttonSize)
button.setImage(UIImage(named: b.imageName), for: .normal)
button.imageView?.contentMode = .scaleAspectFit
button.layer.cornerRadius = Self.buttonSize / 2
button.clipsToBounds = true
button.alpha = b.platform.isInstalled ? 1.0 : 0.4
button.tag = index
button.addTarget(self, action: #selector(handleButtonTap(_:)), for: .touchUpInside)
buttonsContainer.addSubview(button)
let label = UILabel(frame: CGRect(x: x, y: Self.buttonSize + 8, width: Self.buttonSize, height: 20))
label.text = b.title
label.textAlignment = .center
label.font = .systemFont(ofSize: 13)
label.textColor = .darkGray
buttonsContainer.addSubview(label)
}
}
// MARK: - 动画
private func animateIn() {
UIView.animate(withDuration: Self.animationDuration) {
self.backgroundColor = UIColor(white: 0, alpha: 0.5)
var frame = self.contentView.frame
frame.origin.y = UIScreen.main.bounds.height - Self.panelHeight
self.contentView.frame = frame
}
}
private func dismiss(then action: (() -> Void)? = nil) {
UIView.animate(withDuration: Self.animationDuration, animations: {
self.backgroundColor = UIColor(white: 0, alpha: 0)
var frame = self.contentView.frame
frame.origin.y = UIScreen.main.bounds.height
self.contentView.frame = frame
}, completion: { _ in
self.removeFromSuperview()
action?()
})
}
// MARK: - 事件
@objc private func handleBackgroundTap(_ gesture: UITapGestureRecognizer) {
let point = gesture.location(in: self)
if !contentView.frame.contains(point) {
dismiss()
}
}
@objc private func handleButtonTap(_ sender: UIButton) {
let platforms: [SharePlatform] = [WechatShare.shared, QQShare.shared, DouyinShare.shared]
let platform = platforms[sender.tag]
let scene: ShareScene = content.sharefriend == "2" ? .timeline : .friend
let snapshotContent = content
dismiss { [weak platform] in
guard let platform else { return }
Self.log.debug("share via \(platform.name, privacy: .public)")
platform.share(snapshotContent, scene: scene)
}
}
}