【问题标题】:How to pass Textfield value to view controller through button click in Swift UI如何通过 Swift UI 中的按钮单击将 Textfield 值传递给视图控制器
【发布时间】:2021-12-24 19:09:23
【问题描述】:

我在让 Admob 横幅的简单 UI 使用 swiftUI 和视图控制器工作时遇到问题。

控制器:

import UIKit
import Foundation
import GoogleMobileAds
import ToastViewSwift


public class AdsScreenViewController: UIViewController, GADBannerViewDelegate {
   
    var auID = ""
   
    init (auID: String){
        self.auID = auID
        super.init(nibName: nil, bundle: nil)
    }
   
   
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
   
    public override func viewDidLoad() {
        super.viewDidLoad()
       
        var bannerView: GADBannerView!
       
        bannerView = GADBannerView(adSize: kGADAdSizeBanner)

        addBannerViewToView(bannerView)

        bannerView.adUnitID = auID
        bannerView.rootViewController = self

        bannerView.load(GADRequest())

        bannerView.delegate = self

       
    }

    func addBannerViewToView(_ bannerView: GADBannerView) {
    bannerView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(bannerView)
    view.addConstraints(
      [NSLayoutConstraint(item: bannerView,
                          attribute: .bottom,
                          relatedBy: .equal,
                          toItem: bottomLayoutGuide,
                          attribute: .top,
                          multiplier: 1,
                          constant: 0),
       NSLayoutConstraint(item: bannerView,
                          attribute: .centerX,
                          relatedBy: .equal,
                          toItem: view,
                          attribute: .centerX,
                          multiplier: 1,
                          constant: 0)
      ])
   }

public func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
  print("bannerViewDidReceiveAd")
}

public func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
  print("bannerView:didFailToReceiveAdWithError: \(error.localizedDescription)")
}

public func bannerViewDidRecordImpression(_ bannerView: GADBannerView) {
  print("bannerViewDidRecordImpression")
}

public func bannerViewWillPresentScreen(_ bannerView: GADBannerView) {
  print("bannerViewWillPresentScreen")
}

public func bannerViewWillDismissScreen(_ bannerView: GADBannerView) {
  print("bannerViewWillDIsmissScreen")
}

public func bannerViewDidDismissScreen(_ bannerView: GADBannerView) {
  print("bannerViewDidDismissScreen")
}

}

Swift 用户界面:

import SwiftUI
import UIKit


struct TestAdsView: View {
    @State private var auID = ""
    @State private var auType = 1
    @State private var isPresented = false

    var body: some View {
        List {
            VStack(alignment: .leading, content: {
                Text("AdUnit")
                    .font(.footnote).fontWeight(.medium)
                TextField("adunitid", text: $auID)
                    .font(.headline)
            })
            VStack(alignment: .leading, content: {
                    Button(action: {
                        self.auID = auID
                    }, label: {
                        HStack {
                            Text("Show Ad")
                        }
                    })
       
        BannerViewController(auID: auID)
 
    }
   )}
}


struct TestAdsView_Previews: PreviewProvider {
    static var previews: some View {
        TestAdsView()
    }
}

struct BannerViewController: UIViewControllerRepresentable {
    var auID: String
   
    public typealias UIViewControllerType = UIViewController
   
    func makeUIViewController(context: UIViewControllerRepresentableContext<BannerViewController>) -> BannerViewController.UIViewControllerType {
   
        return AdsScreenViewController(auID: auID)

    }

    func updateUIViewController(_ uiViewController: BannerViewController.UIViewControllerType, context: UIViewControllerRepresentableContext<BannerViewController>) {
       
        let controller = AdsScreenViewController(auID: auID)

        controller.auID = self.auID
    }
}

一切编译正常,运行时显示 TextView。但是当输入 id 时它没有调用bannerView.load,我认为 UIViewControllerRepresentable 会像视图上的侦听器一样自动更新,应该调用它但什么都没有发生。

【问题讨论】:

  • @loremipsum 已经尝试了两天,试图让它与 Admob 回调一起工作。尝试了大多数这些链接并到达了这里。对ios开发不是很熟悉,只需要一些指导就可以了。谢谢
  • 为什么会这样?输入 id 时,您的代码的哪一部分应该调用 load?到目前为止,它只在viewDidLoad 时调用,所以你调用它一次,id 为空。
  • 另外,您不应该尝试更换updateUIViewControllermake 上的控制器,然后在其余时间更新或调用方法。 SO中有很多关于如何实施谷歌广告的例子。
  • 如果您在BannerViewController 中将var auID: String 替换为@Binding var auID: String,您可能会使其正常工作,但您可能会被Google 标记,因为您将重新创建广告的次数过多。因为let controller = AdsScreenViewController(auID: auID) on updateUIViewController

标签: ios swift uiviewcontroller admob


【解决方案1】:

这是一个小样本。就像我说的,我现在什么都没有 Google Ads,但应该很简单

import SwiftUI
struct TestAdsView: View {
    @StateObject var vm: AdsScreenViewModel = AdsScreenViewModel()
    var body: some View {
        VStack{
            Text(vm.adStatus.rawValue)
            TextField("adId", text: $vm.adUnitId)
            Button("load Ads", action: {
                vm.loadAds()
            })
            //You might have to play with the position of this.
            AdsScreenView_UI(viewModel: vm).frame(width: 0, height: 0)
        }
    }
}
//This is the source of truth the user input will be held here
class AdsScreenViewModel: ObservableObject, MyAdsViewModelProtocol{
    ///reference to UIKit
    var uiViewController: MyAdsViewControllerProtocol? = nil
    
    @Published var adUnitId: String = ""
    @Published var adStatus: AdStatus = .unknown
    //MARK: MyAdsViewControllerProtocol
    func loadAds() {
        print(#function)
        uiViewController?.loadAds()
    }
    
    func setAdStatus(adStatus: AdStatus) {
        print(#function)
        self.adStatus = adStatus
    }
    func getAdId() -> String {
        print(#function)
        return adUnitId
    }
    
}
struct AdsScreenView_UI: UIViewControllerRepresentable{
    @ObservedObject var viewModel: AdsScreenViewModel
    func makeUIViewController(context: Context) -> some AdsScreenViewController {
        print(#function)
        return AdsScreenViewController(viewModel: viewModel)
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        print(#function)
    }
}
//This can mirror the google sample
class AdsScreenViewController: UIViewController, MyAdsViewControllerProtocol {
    ///SwiftUI Delegate
    var viewModel: MyAdsViewModelProtocol
    
    init(viewModel: MyAdsViewModelProtocol) {
        print(#function)
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: .main)
        //Link between UIKit and SwiftUI
        self.viewModel.uiViewController = self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        print(#function)
        viewModel.setAdStatus(adStatus: .initialized)
        //Put the current code you have here
    }
    //MARK: MyAdsViewModelProtocol
    func loadAds() {
        print(#function)
        print("ad id \(viewModel.getAdId())")
        viewModel.setAdStatus(adStatus: .loading)
        //You would load here not in viewDidLoad
    }
}
//Protocols aren't needed but it makes the code reusable and you can see the connection protocol = interface
protocol MyAdsViewModelProtocol{
    ///Reference to the google view controller
    var uiViewController: MyAdsViewControllerProtocol? { get set }
    
    ///Tells the viewController to load the ad
    func loadAds()
    ///Retrieves the AdId
    func getAdId() -> String
    ///Sets the Ad Status
    func setAdStatus(adStatus: AdStatus)
}
protocol MyAdsViewControllerProtocol: UIViewController{
    ///Reference to the SwiftUI ViewModel
    var viewModel: MyAdsViewModelProtocol { get set }
    ///Tells Google to load the ad
    func loadAds()
}
enum AdStatus: String{
    case initialized
    case loading
    case unknown
    case error
}

struct TestAdsView_Previews: PreviewProvider {
    static var previews: some View {
        TestAdsView()
    }
}

【讨论】:

  • 谢谢,我会试一试的。只是现在真的病了。第一次尝试可能会在明天。真的很感激
  • 由于某种原因,在 AdsScreenViewModel 类中没有调用 uiViewController?.loadAds()。所以打印出来的只是模型类中的 loadAds 函数。
  • @Sam 你改代码了吗?在视图控制器初始化中,您必须在视图模型中设置 uiViewController 变量。看看我代码中的注释,上面写着“UIKit 和 SwiftUI 之间的链接”
  • @Sam 在文本字段中输入 Abc 并按下按钮加载广告后,我发布了我的控制台屏幕截图。
  • 非常感谢。糟糕的是,我没有将 AdsScreenViewUI 包含到 swift ui 中。我正在写它,同时看起来不是复制和粘贴来理解它。现在只需要弄清楚如何分离成单独的文件并添加我将传递给您的视图模型的广告类型。再次感谢您。
猜你喜欢
  • 2018-01-17
  • 2018-01-29
  • 2014-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 2021-08-16
  • 2014-04-08
相关资源
最近更新 更多