Swift中切换SegmentedControl时阻止AdMob Banner重复加载
首先,你当前的问题核心在于手动调用loadView()和viewDidLoad()——这两个是UIViewController的生命周期方法,本该由iOS系统自动调用,手动触发会导致整个视图层级被重新初始化,自然会让AdMob Banner跟着重复加载。我们需要换一种更合理的方式更新按钮,而不是重建整个视图。
具体修改步骤:
1. 抽离按钮创建逻辑为单独方法
把原来写在viewDidLoad里的按钮创建代码,提取成一个独立的方法,比如setupButtons()。这样我们可以在需要更新按钮时直接调用这个方法,而不用重建整个视图。
2. 修正AdMob Banner的创建逻辑
你当前的代码里,admobView是类的属性,但在viewDidLoad里又重新声明了一个局部变量var admobView = GADBannerView(),这会导致类属性没有被正确赋值,而且重复创建。我们要确保Banner只在viewDidLoad里创建一次,并且添加到视图上后不再重复操作。
3. 重写类别切换的处理方法
在handleButtonsChange里,更新currentArray后,先移除之前的所有按钮,再调用setupButtons()创建新按钮,完全不需要调用生命周期方法。
修改后的完整代码示例:
import UIKit import AVFoundation import GoogleMobileAds class ViewController: UIViewController { var spaceForiPhoneX = 0 var fontOfText = 9 var admobView: GADBannerView! // true:测试 let AdMobTest: Bool = true var player = AVAudioPlayer() var buttonArray: [UIButton] = [] // 改成非可选类型,初始化为空数组 var currentArray: [String] = [] var first = true override var prefersStatusBarHidden: Bool { return true } lazy var buttonSegmentedControl: UISegmentedControl = { let sc = UISegmentedControl(items: ["outou","outou2","bougen","meigen"]) sc.translatesAutoresizingMaskIntoConstraints = false sc.tintColor = UIColor.white sc.selectedSegmentIndex = 0 sc.addTarget(self, action: #selector(handleButtonsChange), for: .valueChanged) return sc }() @objc func handleButtonsChange() { switch buttonSegmentedControl.selectedSegmentIndex { case 0: currentArray = outou case 1: currentArray = outou2 case 2: currentArray = bougen case 3: currentArray = meigen default: print("not selected") } // 移除旧按钮 removeAllButtons() // 创建新按钮 setupButtons() } override func viewDidLoad() { super.viewDidLoad() if first { currentArray = reloadViewController.outou first = false print("Google Mobile Ads SDK version: \(GADRequest.sdkVersion())") // 初始化AdMob Banner,只做一次 admobView = GADBannerView(adSize: kGADAdSizeBanner) admobView.frame.origin = CGPoint(x: 0, y: self.view.frame.size.height - admobView.frame.height + CGFloat(spaceForiPhoneX)) admobView.frame.size = CGSize(width: self.view.frame.width, height: admobView.frame.height) admobView.adUnitID = TEST_ID admobView.rootViewController = self admobView.load(GADRequest()) view.addSubview(admobView) // 这里应该添加到view上,而不是navigationItem.titleView?根据你的布局需求调整 } view.backgroundColor = UIColor(r: 61, g: 91, b: 151) // 初始化按钮 setupButtons() // 添加SegmentedControl并设置约束 view.addSubview(buttonSegmentedControl) setupButtonSegmentedControl() } // 单独的按钮创建方法 func setupButtons() { let numberOfrows = 6 let numberOfLines = 3 var count = 0 let sizeOfButton = view.frame.height / 10 let widthOfSpace = ((view.frame.width - sizeOfButton * 4) / 5 ) for row in 0 ... numberOfrows { for line in 0 ... numberOfLines { guard count < currentArray.count else { break } let button = UIButton(type: .custom) button.backgroundColor = UIColor(r: 1, g: 101, b: 161) button.setTitleColor(UIColor.white, for: .normal) button.titleLabel?.numberOfLines = 0 button.setTitle(currentArray[row * 4 + line], for: .normal) button.frame = CGRect(x: (widthOfSpace + sizeOfButton) * CGFloat(line) + widthOfSpace, y: view.frame.height / 9.3 * CGFloat(row) + 50, width: sizeOfButton ,height: sizeOfButton ) button.layer.cornerRadius = 10 button.layer.borderColor = UIColor.white.cgColor button.layer.borderWidth = 1 button.titleLabel?.font = UIFont.boldSystemFont(ofSize: CGFloat(fontOfText)) button.tag = row * 4 + line button.addTarget(self, action: #selector(buttonPushed), for: .touchUpInside) button.showsTouchWhenHighlighted = true buttonArray.append(button) view.addSubview(button) count += 1 } } } // 移除所有旧按钮的方法 func removeAllButtons() { buttonArray.forEach { button in button.removeFromSuperview() } buttonArray.removeAll() } @objc func buttonPushed(button: UIButton) { guard let audioPath = Bundle.main.path(forResource: currentArray[button.tag], ofType: "mp3") else { print("音频文件不存在") return } do { try player = AVAudioPlayer(contentsOf: URL(fileURLWithPath: audioPath)) player.play() } catch { print(error) } } func setupButtonSegmentedControl() { buttonSegmentedControl.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true buttonSegmentedControl.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -90).isActive = true buttonSegmentedControl.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -24).isActive = true buttonSegmentedControl.heightAnchor.constraint(equalToConstant: 60).isActive = true } }
关键说明:
- 不再调用生命周期方法:
handleButtonsChange里只负责更新数据源、移除旧按钮、创建新按钮,完全避免了视图重建。 - Banner只初始化一次:AdMob的
admobView在viewDidLoad里只创建并添加一次,后续切换类别不会再触碰它,自然不会重复加载。 - 按钮管理更清晰:通过
buttonArray保存所有按钮,移除时可以批量操作,避免内存泄漏和视图层级混乱。
内容的提问来源于stack exchange,提问作者shinya
相关产品推荐
相关产品推荐

