import Cocoa import WebKit protocol AdsWebViewDelegate: AnyObject { func kmAdViewClicked(_ adView: KMAdsWebView) func kmAdViewClose(_ adView: KMAdsWebView) } enum KMADViewDirections: Int { case up case down } // Banner Ads Width,Height let kAD_View_Width = 728.0 let kAD_View_Height = 90.0 // Banner Ads refresh rate in second, 新广告会自动刷新,所以时间延长 let kAD_Refresh_Rate = 6000.0 class KMAdsWebView: NSView, WKNavigationDelegate, CAAnimationDelegate { weak var adDelegate: AdsWebViewDelegate? private var closeButton: NSButton! var clickButton: NSButton! private var currentPage: Int = 0 private var completionHandler: ((Int) -> Void)? var adsImageView: NSImageView! var adsInfo: KMAdsInfo! var adPosY: CGFloat = 30.0 override init(frame frameRect: NSRect) { super.init(frame: frameRect) wantsLayer = true self.frame = NSRect(x: 0, y: 0, width: kAD_View_Width, height: kAD_View_Height) autoresizingMask = [.minXMargin, .maxXMargin] adsImageView = NSImageView.init(frame: self.bounds) adsImageView.autoresizingMask = [.width, .height] adsImageView.imageScaling = .scaleAxesIndependently addSubview(adsImageView) adPosY = 30.0 clickButton = NSButton.init(frame: self.bounds) clickButton.isHidden = false clickButton.autoresizingMask = [.width, .height] clickButton.isBordered = false clickButton.title = "" clickButton.target = self clickButton.action = #selector(buttonItemClicked(_:)) addSubview(clickButton) closeButton = NSButton.init(frame: NSRect(x: frameRect.size.width - 30, y: frameRect.size.height - 30, width: 30, height: 30)) closeButton.isHidden = false closeButton.imagePosition = .imageOnly closeButton.imageScaling = .scaleProportionallyUpOrDown closeButton.autoresizingMask = [.maxXMargin, .minYMargin] closeButton.image = NSImage(named: "ad_cancel_button00") closeButton.isBordered = false closeButton.target = self closeButton.action = #selector(buttonItemClicked_Close(_:)) addSubview(closeButton) NotificationCenter.default.addObserver(self, selector: #selector(handleUserHaveClickRateUsNotification(_:)), name: NSNotification.Name("kUserHaveClickRateUsNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(recommondInfoUpdateNoti), name: NSNotification.Name("KMRecommondInfoUpdateNoti"), object: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { adDelegate = nil completionHandler = nil NotificationCenter.default.removeObserver(self) } func restoreDefaultAdPosY() { adPosY = 30.0 } func reloadData() { DispatchQueue.main.async { self.adsImageView.image = self.adsInfo.adsImage } } func resizeWithOldSuperviewSize(oldSize: NSSize) { var width = kAD_View_Width var height = kAD_View_Height if superview?.frame.size.width ?? 0 < width { let newWidth = (superview?.frame.size.width ?? 0) - 20 height = height / width * newWidth width = newWidth } self.frame = NSRect(x: (superview?.frame.size.width ?? 0 - width) / 2.0, y: frame.origin.y, width: width, height: height) } func beginSheetModalForView(view: NSView, directions: KMADViewDirections, animated: Bool, completionHandler handler: ((Int) -> Void)?) { self.completionHandler = handler if adPosY < 0 { restoreDefaultAdPosY() } let tWidth = frame.size.width let tHeight = frame.size.height view.addSubview(self) if animated { switch directions { case .up: frame = NSRect(x: (view.frame.size.width - tWidth) / 2.0, y: view.frame.size.height, width: tWidth, height: tHeight) animator().frame = NSRect(x: frame.origin.x, y: view.frame.size.height - tHeight - adPosY, width: tWidth, height: tHeight) case .down: frame = NSRect(x: (view.frame.size.width - tWidth) / 2.0, y: -tHeight, width: tWidth, height: tHeight) animator().frame = NSRect(x: frame.origin.x, y: adPosY, width: tWidth, height: tHeight) default: frame = NSRect(x: 0, y: 0, width: tWidth, height: tHeight) } } else { switch directions { case .up: frame = NSRect(x: (view.frame.size.width - tWidth) / 2.0, y: view.frame.size.height - tHeight - adPosY, width: tWidth, height: tHeight) case .down: frame = NSRect(x: (view.frame.size.width - tWidth) / 2.0, y: adPosY, width: tWidth, height: tHeight) default: frame = NSRect(x: 0, y: 0, width: tWidth, height: tHeight) } } adsImageView.frame = bounds closeButton.frame = NSRect(x: self.bounds.size.width - 30, y: self.bounds.size.height - 30, width: 30, height: 30) currentPage = 0 } @objc func recommondInfoUpdateNoti() { DispatchQueue.main.async { self.reloadData() } } //MARK: - Action @objc func buttonItemClicked_Open(_ sender: Any) { guard let newURL = (sender as? NSURL) else { return } NSWorkspace.shared.open(newURL as URL) adDelegate?.kmAdViewClicked(self) if let completionHandler = self.completionHandler { completionHandler(currentPage + 1) } removeFromSuperview() } @objc func buttonItemClicked(_ sender: Any) { guard let string = self.adsInfo?.adsURLLink else { return } let newURL = NSURL(string: string) NSWorkspace.shared.open(newURL! as URL) if let completionHandler = self.completionHandler { completionHandler(currentPage + 1) } adDelegate?.kmAdViewClicked(self) } @objc func buttonItemClicked_Close(_ sender: Any) { adDelegate?.kmAdViewClose(self) if let completionHandler = self.completionHandler { completionHandler(0) } removeFromSuperview() } //MARK: - CAAnimation func animationDidStart(_ anim: CAAnimation) { } func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { if !flag { return } closeButton.alphaValue = 1.0 adsImageView.frame = bounds } // MARK: - WKNavigationDelegate @objc func handleUserHaveClickRateUsNotification(_ notification: Notification) { } }