筆者性懶,腹中無墨
關(guān)于簡單的可拖動(dòng)的頁面浮層廣告,可以用addSubview實(shí)現(xiàn).遵循MVC設(shè)計(jì)模式,subview分離出來.筆者的思路是在自定義的view上放一個(gè)UIImageView,沒有直接加UIImageView.由于浮層廣告可以是動(dòng)圖,所以需要支持動(dòng)圖的一個(gè)三方的庫FLAnimatedImage.GitHub源碼:https://github.com/Flipboard/FLAnimatedImage.當(dāng)然,SDWebImage在4.0版本之后,加入了依賴FLAnimatedImage,支持動(dòng)圖.共同點(diǎn)都是原來的UIImageView都需要換成FLAnimatedImageView.
簡要概括為以下幾個(gè)步驟
1.廣告的拖動(dòng)手勢
給UIView上的FLAnimatedImageView添加輕觸Tap和拖動(dòng)Pan手勢,點(diǎn)擊跳轉(zhuǎn)廣告的URL鏈接,拖動(dòng)改變廣告在俯視圖中的位置,一般浮層廣告frame較小.根據(jù)需求可能會(huì)有拖動(dòng)范圍的限制,例如導(dǎo)航欄以下,tabbar之上.iPhone X就需要考慮尺寸問題,所以需要考慮距頂部距底部的高度.
/* 廣告拖動(dòng)手勢 */
@objc fileprivate func panAdvertise(ges: UIPanGestureRecognizer) {
var startPoint: CGPoint = CGPoint.zero
switch ges.state {
case .began:
startPoint = ges.location(in: superView)
UIView.animate(withDuration: 0.2, animations: {
self.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
self.alpha = 0.7
})
case .changed:
let newPoint: CGPoint = ges.location(in: superView)
//由于測試圖片是方形,所以圖片在邊界時(shí)的中心點(diǎn)距離邊界圖片尺寸的1/2
let imageHeight = advertiseImage.frame.size.height*0.5
//為了方便,將浮層的superview是作為參數(shù)傳入
let superFrameW = superView.frame.size.width
let superFrameH = superView.frame.size.height
var deltaX: CGFloat = 0
var deltaY: CGFloat = 0
deltaX = newPoint.x - startPoint.x
deltaY = newPoint.y - startPoint.y
//左邊界
if newPoint.x < superView.frame.origin.x + imageHeight {
deltaX = imageHeight
}
//右邊界
if newPoint.x > superFrameW - imageHeight {
deltaX = superFrameW - imageHeight
}
//上邊界
if newPoint.y < topHeight + imageHeight {
deltaY = imageHeight + topHeight
}
//下邊界
if newPoint.y > superFrameH - topHeight {
deltaY = superFrameH - bottomHeight - imageHeight
}
self.center = CGPoint(x: deltaX, y: deltaY)
case .ended, .cancelled, .failed:
UIView.animate(withDuration: 0.2, animations: {
self.transform = CGAffineTransform.identity
self.alpha = 1.0
})
case .possible:
break
}
}
2.獲取廣告數(shù)據(jù)并展示廣告
/*
獲取廣告基本數(shù)據(jù)
這里的廣告數(shù)據(jù) advertises 并沒有區(qū)分動(dòng)圖或者圖片,只是獲得了廣告展示位的基本信息,類似title,url等
var advertises = [Advertise]() 這里的結(jié)構(gòu)體類型Advertise根據(jù)需求定義
*/
fileprivate func getAdvertises() {
ADSuspendService.shared.getSuspendAdvertise({ (advertises) in
//advertises是廣告結(jié)構(gòu)體類型的數(shù)組
self.advertises = advertises
if advertises.count > 0 {
//根據(jù)廣告的基本信息,獲取圖片或者動(dòng)圖
self.getSuspendImage()
self.addSubview(self.advertiseImage)
self.advertiseImage.snp.makeConstraints { (make) in
make.left.right.top.bottom.equalTo(self)
}
}
})
}
// 根據(jù)廣告類型 展示廣告
fileprivate func getSuspendImage() {
/*currentIndex表示當(dāng)前展示的第幾個(gè)浮層廣告,由于需求是一定的時(shí)間間隔切換浮層廣告,所以需要記錄每次展示的廣告,無需求可忽略*/
if let url = advertises[currentIndex].url {
self.getFLAnimatedImage(from: url, callback: { (img) in
DispatchQueue.main.async {
if ((img as? UIImage) != nil) {
self.advertiseImage.image = img as? UIImage
self.advertiseImage.animatedImage = nil
}else if (img as? FLAnimatedImage) != nil {
self.advertiseImage.animatedImage = img as? FLAnimatedImage
}
/* 統(tǒng)計(jì)浮層廣告曝光,根據(jù)需求是否統(tǒng)計(jì)*/
var event:NativeClickEvent
event = event_ad_show
event.areaLabelName = self.advertises[self.currentIndex].spot
event.desc = self.advertises[self.currentIndex].adId
EventTracker.trackNativeClickEvent(event: event)
}
})
}
}
//獲取廣告類型
fileprivate func getFLAnimatedImage(from url: String, callback: @escaping (Any?) -> Void) {
guard let urlObj = URL(string: url) else {
callback(nil)
return
}
//CachedResourceManager是自己的緩存管理器,根據(jù)url有緩存則不在請求,類似的可以使用邊便捷的SDWebImage的方法,關(guān)于CachedResourceManager這里就不在贅述.
let cacheManager = CachedResourceManager()
cacheManager.downloadResource(urlObj, forceCaching: true) { (data) in
if let data = data, let image = FLAnimatedImage(gifData: data) { // Gif
callback(image)
}else if let data = data, let image = UIImage(data: data, scale: UIScreen.main.scale) { // jpg/png
callback(image)
}else {
callback(nil)
}
}
}
3.關(guān)于計(jì)時(shí)器
//TODO: - 測試間隔10秒鐘
func startTimer() {
//這個(gè)時(shí)間可以是離開浮層廣告頁面的時(shí)間.或者根據(jù)需求,記錄就可以
if let last = ADSuspendService.shared.lastedADShownDate {
let minute = Date().timeIntervalSince(last)
if minute >= 10 && advertises.count > 1 {
if currentIndex < advertises.count - 1 {
currentIndex += 1
} else {
currentIndex = 0
}
self.getSuspendImage()
}
} else {
// 若未超過10分鐘,則會(huì)從零計(jì)時(shí),啟動(dòng)計(jì)時(shí)器,到10分鐘才換廣告
}
if advertises.count > 1 {
timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(changeImage), userInfo: nil, repeats: true)
}
}
func changeImage() {
if currentIndex < advertises.count - 1 {
currentIndex += 1
} else {
currentIndex = 0
}
getSuspendImage()
}
4.初始化view
init(frame: CGRect, top: CGFloat, bottom: CGFloat) {
super.init(frame: frame)
topHeight = top
bottomHeight = bottom
DispatchQueue.global().async {
self.getAdvertises()
self.startTimer()
}
}
5.廣告的點(diǎn)擊跳轉(zhuǎn)及統(tǒng)計(jì)就不再贅述,離開浮層頁需要移除定時(shí)器,回到該頁面需要再次開啟定時(shí)器.
func removeTimer() {
timer?.invalidate()
timer = nil
}
deinit {
timer?.invalidate()
timer = nil
}
6.Controller里
fileprivate func suspendAdvertiseShow() {
adView = AdvertiseSuspendView(frame: CGRect.zero, top: (navigationController?.navigationBar.frame.size.height)! + UIApplication.shared.statusBarFrame.size.height, bottom: (tabBarController?.tabBar.frame.size.height)!)
adView.superView = view
scrollContentView.addSubview(adView)
adView.snp.makeConstraints { (make) in
make.trailing.equalTo(networkPanel).offset(-15)
make.bottom.equalTo(networkPanel).offset(-15)
make.size.equalTo(kScreenWidth*0.16)
}
adView.advertiseTapHandler = { url in
self.clickAdvertiseHandler(url: url)
}
}
最后,浮層廣告的曝光量和點(diǎn)擊量應(yīng)該是運(yùn)營考慮的重點(diǎn),埋點(diǎn)統(tǒng)計(jì)工作要做到.end.