// // SelfRenderView.swift // RoderickRalph // // Created by Neoa on 2025/1/11. // import UIKit import AnyThinkNative // 定义常量(混淆命名) let kNRPaneW = UIScreen.main.bounds.width let kNRPaneH: CGFloat = 170 let kNRMediaW = UIScreen.main.bounds.width let kNRMediaH: CGFloat = 160 class RenderUnitView: UIView { // MARK: - UI Components /// 广告主 var advLabel: UILabel! /// 内容 var bodyLabel: UILabel! /// 标题 var headlineLabel: UILabel! /// 类似"点我下载"按钮上面的口号 var actionLabel: UILabel! /// 评分 var scoreLabel: UILabel! /// 推广应用icon var badgeImageView: UIImageView! /// 主图 var heroImageView: UIImageView! /// 广告平台logo标识 var brandLogoView: UIImageView! /// 关闭按钮 var closeControl: UIButton! /// 媒体视图 var mediaContainer: UIView? // only for yandex native var hostLabel: UILabel! var alertLabel: UILabel! // MARK: - Private Properties private var adPayload: ATNativeAdOffer? // MARK: - Initialization override init(frame: CGRect) { super.init(frame: frame) configureSurface() } required init?(coder: NSCoder) { super.init(coder: coder) configureSurface() } convenience init(offer: ATNativeAdOffer) { self.init(frame: .zero) self.adPayload = offer configureSurface() } deinit { print("🔥---RenderUnitView--dealloc") } // MARK: - UI Setup private func configureSurface() { // 随机背景色 backgroundColor = makeRandomTint() assembleSubviews() constrainSubviews() applyOfferContent() } private func assembleSubviews() { // // 广告主标签 // advertiserLabel = UILabel() // advertiserLabel.font = UIFont.boldSystemFont(ofSize: 15.0) // advertiserLabel.textColor = .black // advertiserLabel.textAlignment = .left // advertiserLabel.translatesAutoresizingMaskIntoConstraints = false // addSubview(advertiserLabel) // 标题标签 headlineLabel = UILabel() headlineLabel.font = UIFont.systemFont(ofSize: 14.0) headlineLabel.textColor = .gray headlineLabel.textAlignment = .left headlineLabel.translatesAutoresizingMaskIntoConstraints = false addSubview(headlineLabel) // 内容标签 bodyLabel = UILabel() bodyLabel.font = UIFont.boldSystemFont(ofSize: 16.0) bodyLabel.textColor = .black bodyLabel.numberOfLines = 0 bodyLabel.translatesAutoresizingMaskIntoConstraints = false addSubview(bodyLabel) // // CTA标签 // ctaLabel = UILabel() // ctaLabel.font = UIFont.systemFont(ofSize: 15.0) // ctaLabel.textColor = .white // ctaLabel.backgroundColor = .blue // ctaLabel.textAlignment = .center // ctaLabel.translatesAutoresizingMaskIntoConstraints = false // addSubview(ctaLabel) // // // 评分标签 // ratingLabel = UILabel() // ratingLabel.font = UIFont.systemFont(ofSize: 15.0) // ratingLabel.textColor = .systemYellow // ratingLabel.translatesAutoresizingMaskIntoConstraints = false // addSubview(ratingLabel) // // 域名标签 (yandex) // domainLabel = UILabel() // domainLabel.font = UIFont.systemFont(ofSize: 15.0) // domainLabel.textColor = .black // domainLabel.translatesAutoresizingMaskIntoConstraints = false // addSubview(domainLabel) // // // 警告标签 (yandex) // warningLabel = UILabel() // warningLabel.font = UIFont.systemFont(ofSize: 15.0) // warningLabel.textColor = .black // warningLabel.translatesAutoresizingMaskIntoConstraints = false // addSubview(warningLabel) // // 图标 // iconImageView = UIImageView() // iconImageView.layer.cornerRadius = 4.0 // iconImageView.layer.masksToBounds = true // iconImageView.contentMode = .scaleAspectFit // iconImageView.translatesAutoresizingMaskIntoConstraints = false // addSubview(iconImageView) // 主图 heroImageView = UIImageView() heroImageView.contentMode = .scaleAspectFit heroImageView.translatesAutoresizingMaskIntoConstraints = false addSubview(heroImageView) // Logo // logoImageView = UIImageView() // logoImageView.contentMode = .scaleAspectFit // logoImageView.tag = 110220 // logoImageView.translatesAutoresizingMaskIntoConstraints = false // addSubview(logoImageView) // 关闭按钮 closeControl = UIButton(type: .custom) closeControl.backgroundColor = .white // 尝试从AnyThinkSDK bundle中获取关闭按钮图片 // if let bundlePath = Bundle.main.path(forResource: "AnyThinkSDK", ofType: "bundle"), // let bundle = Bundle(path: bundlePath), // let closeImg = UIImage(named: "offer_video_close", in: bundle, compatibleWith: nil) { // dislikeButton.setImage(closeImg, for: .normal) // } else { closeControl.setTitle("×", for: .normal) closeControl.setTitleColor(.gray, for: .normal) closeControl.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18) // } closeControl.translatesAutoresizingMaskIntoConstraints = false addSubview(closeControl) enableInteractions() } private func enableInteractions() { // ctaLabel.isUserInteractionEnabled = true // advertiserLabel.isUserInteractionEnabled = true headlineLabel.isUserInteractionEnabled = true bodyLabel.isUserInteractionEnabled = true // ratingLabel.isUserInteractionEnabled = true // domainLabel.isUserInteractionEnabled = true // warningLabel.isUserInteractionEnabled = true // iconImageView.isUserInteractionEnabled = true heroImageView.isUserInteractionEnabled = true // logoImageView.isUserInteractionEnabled = true } private func applyOfferContent() { guard let offer = adPayload else { return } // 设置图标 // if let icon = offer.nativeAd.icon { // iconImageView.image = icon // } else if let iconUrl = offer.nativeAd.iconUrl, let url = URL(string: iconUrl) { // loadImage(from: url, into: iconImageView) // print("🔥AnyThinkDemo::iconUrl:\(iconUrl)") // } // 设置主图 if let mainImage = offer.nativeAd.mainImage { heroImageView.image = mainImage } else if let imageUrl = offer.nativeAd.imageUrl, let url = URL(string: imageUrl) { fetchImage(from: url, into: heroImageView) print("🔥AnyThinkDemo::imageUrl:\(imageUrl)") } // // 设置Logo // if let logoUrl = offer.nativeAd.logoUrl, !logoUrl.isEmpty { // print("🔥----logoUrl:\(logoUrl)") // if let url = URL(string: logoUrl) { // loadImage(from: url, into: logoImageView) // } // } else if let logo = offer.nativeAd.logo { // print("🔥----logo:\(logo)") // logoImageView.image = logo // } else { // // 根据networkFirmID设置默认平台logo //// if offer.networkFirmID == ATNetworkFirmIDTypeCSJ { //// logoImageView.image = UIImage(named: "tt_ad_logo_new") //// } // } // 设置文本内容 // advertiserLabel.text = offer.nativeAd.advertiser headlineLabel.text = offer.nativeAd.title bodyLabel.text = offer.nativeAd.mainText // ctaLabel.text = offer.nativeAd.ctaText ?? "查看详情"// : offer.nativeAd.ctaText // ratingLabel.text = "评分:\(offer.nativeAd.rating?.stringValue ?? "暂无评分")" // yandex平台支持 // domainLabel.text = offer.nativeAd.domain // warningLabel.text = offer.nativeAd.warning print("🔥AnythinkDemo::native domain:\(offer.nativeAd.domain ?? ""); warning:\(offer.nativeAd.warning ?? "")") print("🔥AnythinkDemo::native文本内容title:\(offer.nativeAd.title ?? ""); text:\(offer.nativeAd.mainText ?? ""); cta:\(offer.nativeAd.ctaText ?? "")") } private func constrainSubviews() { backgroundColor = .white headlineLabel.backgroundColor = .white bodyLabel.backgroundColor = .white NSLayoutConstraint.activate([ // 主图约束 heroImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), // mainImageView.centerYAnchor.constraint(equalTo: centerYAnchor), heroImageView.topAnchor.constraint(equalTo: topAnchor, constant: 10), heroImageView.widthAnchor.constraint(equalToConstant: 200), heroImageView.heightAnchor.constraint(equalToConstant: 150), // // Logo约束 // logoImageView.heightAnchor.constraint(equalToConstant: 25), // logoImageView.widthAnchor.constraint(equalToConstant: 25), // logoImageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5), // logoImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), // 内容约束 bodyLabel.leadingAnchor.constraint(equalTo: heroImageView.trailingAnchor, constant: 8), bodyLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -30), // textLabel.heightAnchor.constraint(equalToConstant: 20), bodyLabel.topAnchor.constraint(equalTo: heroImageView.topAnchor, constant: 10), // 标题约束 headlineLabel.leadingAnchor.constraint(equalTo: heroImageView.trailingAnchor, constant: 8), headlineLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -36), headlineLabel.heightAnchor.constraint(equalToConstant: 20), headlineLabel.bottomAnchor.constraint(equalTo: heroImageView.bottomAnchor,constant: 0), // // CTA约束 // ctaLabel.topAnchor.constraint(equalTo: textLabel.bottomAnchor, constant: 5), // ctaLabel.widthAnchor.constraint(equalToConstant: 100), // ctaLabel.heightAnchor.constraint(equalToConstant: ctaLabel.font.lineHeight), // ctaLabel.leadingAnchor.constraint(equalTo: textLabel.leadingAnchor), // // 评分约束 // ratingLabel.leadingAnchor.constraint(equalTo: ctaLabel.trailingAnchor, constant: 20), // ratingLabel.heightAnchor.constraint(equalToConstant: ratingLabel.font.lineHeight), // ratingLabel.topAnchor.constraint(equalTo: ctaLabel.topAnchor), // // // 域名约束 // domainLabel.leadingAnchor.constraint(equalTo: ratingLabel.trailingAnchor, constant: 20), // domainLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -40), // domainLabel.topAnchor.constraint(equalTo: ctaLabel.topAnchor), // domainLabel.heightAnchor.constraint(equalToConstant: 40), // // // 广告主约束 // advertiserLabel.heightAnchor.constraint(equalToConstant: advertiserLabel.font.lineHeight), // advertiserLabel.leadingAnchor.constraint(equalTo: ctaLabel.leadingAnchor), // advertiserLabel.topAnchor.constraint(equalTo: ctaLabel.bottomAnchor, constant: 5), // // // 图标约束 // iconImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20), // iconImageView.heightAnchor.constraint(equalToConstant: 75), // iconImageView.widthAnchor.constraint(equalToConstant: 75), // iconImageView.topAnchor.constraint(equalTo: topAnchor, constant: 20), // // // // 警告标签约束 // warningLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), // warningLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), // warningLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5), // warningLabel.heightAnchor.constraint(equalToConstant: 40), // 关闭按钮约束 closeControl.heightAnchor.constraint(equalToConstant: 20), closeControl.widthAnchor.constraint(equalToConstant: 20), closeControl.topAnchor.constraint(equalTo: topAnchor, constant: 5), closeControl.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10) ]) } // MARK: - Media View func installMediaView(_ mediaView: UIView?) { self.mediaContainer = mediaView if let mediaView = mediaView { addSubview(mediaView) mediaView.translatesAutoresizingMaskIntoConstraints = false // 给mediaView设置约束,与mainImageView相同 NSLayoutConstraint.activate([ mediaView.leadingAnchor.constraint(equalTo: heroImageView.leadingAnchor), mediaView.trailingAnchor.constraint(equalTo: heroImageView.trailingAnchor), mediaView.topAnchor.constraint(equalTo: heroImageView.topAnchor), mediaView.bottomAnchor.constraint(equalTo: heroImageView.bottomAnchor) ]) } } // MARK: - Helper Methods private func makeRandomTint() -> UIColor { let r = CGFloat.random(in: 0...255) let g = CGFloat.random(in: 0...255) let b = CGFloat.random(in: 0...255) let a = CGFloat.random(in: 0...255) return UIColor(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: a/255.0) } private func fetchImage(from url: URL, into imageView: UIImageView) { URLSession.shared.dataTask(with: url) { data, _, _ in guard let data = data, let image = UIImage(data: data) else { return } DispatchQueue.main.async { imageView.image = image } }.resume() } // MARK: - Cleanup func teardown() { // 及时销毁offer adPayload = nil mediaContainer?.removeFromSuperview() mediaContainer = nil } }