The ad that I am trying the load in SwiftUI is scaled much larger than it should be. It should be 320 x 50 but appears to be much more than this. AppLovin doesn't support SwiftUI yet (and they should), so I had to make a representable to make it work for SwiftUI. I have tried many other people's approaches at this, but still get the same result.
Here is the code for the represent able (All I add in the main SwiftUI view is just the reference to to the ad representable)
import Foundation
import SwiftUI
import AppLovinSDK
struct AppLovinBannerAd : UIViewControllerRepresentable {
typealias UIViewControllerType = AppLovinAdViewController
func makeUIViewController(context: Context) -> AppLovinAdViewController {
return AppLovinAdViewController()
}
func updateUIViewController(_ uiViewController: AppLovinAdViewController, context: Context) {
uiViewController.createBannerAd()
}
}
class AppLovinAdViewController: UIViewController, MAAdViewAdDelegate
{
func didDisplay(_ ad: MAAd) {}
func didHide(_ ad: MAAd) {}
var adView: MAAdView!
public func createBannerAd()
{
//Don't worry about my ad id, it is a test id, but without your own SDK key, you won't be able to load it as a test
adView = MAAdView(adUnitIdentifier: "3178a4370cc1ab34")
adView.delegate = self
adView.frame = CGRect(x: 0, y: 0, width: 320, height: 50)
// Set background or background color for banners to be fully functional
adView.backgroundColor = UIColor.white
view.addSubview(adView)
// Load the first ad
adView.loadAd()
}
// MARK: MAAdDelegate Protocol
func didLoad(_ ad: MAAd) { print("Ad is loaded, needs to appear now") }
func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) { print("Ad failed to load") }
func didClick(_ ad: MAAd) {}
func didFail(toDisplay ad: MAAd, withError error: MAError) {}
// MARK: MAAdViewAdDelegate Protocol
func didExpand(_ ad: MAAd) {}
func didCollapse(_ ad: MAAd) {}
}
And this is my output (Sorry the image is large, the banner is what says "Congrats!"):
I have tried changing the frame size: Didn't work
I have tried changing the X and Y position: Slightly worked by showing more of the ad in the specified position, but was still a large size
I have tried using other methods to making the representable: Gets this same result
Am I doing something wrong? Or is this how the SDK send me the ad?
Related
SwiftUI novice here.
My PHPicker results show a weird behaviour.
Whether I pick one image or several, often the result is empty for a single image or incomplete if multiple images are picked.
Oddities: every image that is missing from a PHPicker session result can be fetched in another session (so the image itself is okay), furthermore it happens that in the next session some images are additionally returned that had been selected in the session before but were missing.
There are no explicit error messages in the console, also the behaviour is completely unpredictable.
So let's say I pick 20 images in a session: 9 of them get returned and appended and maybe another 6 of them get returned additionally in the next session without being picked, so there are still 5 images missing which in turn are able to be picked in future sessions.
Further use of the PHPicker results works without problems; dates and paths are sent into Core Data and the images themselves saved to FileManager; this data is then combined in a list view.
I guess it might have to do with the interplay of the 3 parts (date, path, image) I fetch for each image, but I'm at a loss where exactly the problem arises.
struct PhotoPicker: UIViewControllerRepresentable {
#Binding var dates: [Date?]
#Binding var paths: [String?]
#Binding var images: [UIImage?]
#Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
config.filter = .images
config.selectionLimit = 20
config.preferredAssetRepresentationMode = .current
let controller = PHPickerViewController(configuration: config)
controller.delegate = context.coordinator
return controller
}
func makeCoordinator() -> PhotoPicker.Coordinator {
return Coordinator(self)
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
}
class Coordinator: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.presentationMode.wrappedValue.dismiss()
guard !results.isEmpty else {
return
}
print(results)
for result in results {
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
if let assetId = result.assetIdentifier {
let assetResults = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil)
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.image.identifier) {
(url, error) in
if error != nil {
print("error \(error!)");
} else {
result.itemProvider.loadObject(ofClass: UIImage.self) {
(image, error) in
if error != nil {
print("error \(error!)");
} else {
if assetResults.firstObject?.creationDate != nil && url?.lastPathComponent != nil && image != nil {
Task { #MainActor in
self.parent.dates.append(assetResults.firstObject?.creationDate)
print(assetResults.firstObject?.creationDate as Any)
self.parent.paths.append(url?.lastPathComponent)
print(url?.lastPathComponent as Any)
self.parent.images.append(image as? UIImage)
print(image as Any)
}
}
}
}
}
}
}
}
}
}
private let parent: PhotoPicker
init(_ parent: PhotoPicker) {
self.parent = parent
}
}
}
I have created a class to perform a network request and parse the data using Combine. I'm not entirely certain the code is correct, but it's working as of now (still learning the basics of Swift and basic networking tasks). My Widget has the correct data and is works until the data becomes nil. Unsure how to check if the data from my first publisher in my SwiftUI View is nil, the data seems to be valid even when there's no games showing.
My SwiftUI View
struct SimpleEntry: TimelineEntry {
let date: Date
public var model: CombineData?
let configuration: ConfigurationIntent
}
struct Some_WidgetEntryView : View {
var entry: Provider.Entry
#Environment(\.widgetFamily) var widgetFamily
var body: some View {
VStack (spacing: 0){
if entry.model?.schedule?.dates.first?.games == nil {
Text("No games Scheduled")
} else {
Text("Game is scheduled")
}
}
}
}
Combine
import Foundation
import WidgetKit
import Combine
// MARK: - Combine Attempt
class CombineData {
var schedule: Schedule?
var live: Live?
private var cancellables = Set<AnyCancellable>()
func fetchSchedule(_ teamID: Int, _ completion: #escaping (Live) -> Void) {
let url = URL(string: "https://statsapi.web.nhl.com/api/v1/schedule?teamId=\(teamID)")!
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Schedule.self, decoder: JSONDecoder())
//.catch { _ in Empty<Schedule, Error>() }
//.replaceError(with: Schedule(dates: []))
let publisher2 = publisher
.flatMap {
return self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "")
}
Publishers.Zip(publisher, publisher2)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {_ in
}, receiveValue: { schedule, live in
self.schedule = schedule
self.live = live
completion(self.live!)
WidgetCenter.shared.reloadTimelines(ofKind: "NHL_Widget")
}).store(in: &cancellables)
}
func fetchLiveFeed(_ link: String) -> AnyPublisher<Live, Error /*Never if .catch error */> {
let url = URL(string: "https://statsapi.web.nhl.com\(link)")!
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: Live.self, decoder: JSONDecoder())
//.catch { _ in Empty<Live, Never>() }
.eraseToAnyPublisher()
}
}
Like I said in the comments, it's likely that the decode(type: Live.self, decoder: JSONDecoder()) returns an error because the URL that you're fetching from when link is nil doesn't return anything that can be decoded as Live.self.
So you need to handle that case somehow. For example, you can handle this by making the Live variable an optional, and returning nil when link is empty (or nil).
This is just to set you in the right direction - you'll need to work out the exact code yourself.
let publisher2 = publisher1
.flatMap {
self.fetchLiveFeed($0.dates.first?.games.first?.link ?? "")
.map { $0 as Live? } // convert to an optional
.replaceError(with: nil)
}
Then in the sink, handle the nil:
.sink(receiveCompletion: {_ in }, receiveValue:
{ schedule, live in
if let live = live {
// normal treatment
self.schedule = schedule
self.live = live
//.. etc
} else {
// set a placeholder
}
})
SwiftUI and WidgetKit work differently. I needed to fetch data in getTimeline for my IntentTimelineProvider then add a completion handler for my TimelineEntry. Heavily modified my Combine data model. All credit goes to #EmilioPelaez for pointing me in the right direction, answer here.
This question has been rummaged throughout the network. I customized a sizeFont sizeFont inherits UIFont. Can use func systemFont print is also normal and has been set.
But the display has no effect.
My Configuration:
mac OS 11.1
iPhone 14.3
Xcode 12.3
I have tried the following methods.
1.
Create custom NSAttributedString.Key
But no effect
2.Simulator and real machine (No, no effect)
this is my code
import SwiftUI
struct ContentView: View {
var body: some View {
UIkitTextView()
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension NSAttributedString.Key {
static let textStyle: NSAttributedString.Key = .init("textStyle")
}
struct UIkitTextView: UIViewRepresentable {
var fullString: NSMutableAttributedString = NSMutableAttributedString(string: "Hello, World")
func makeUIView(context: Context) -> UITextView {
let view = UITextView()
let attributedtest: [NSAttributedString.Key: Any] = [
.sizefont: UIFont.systemFont(ofSize: 72),
.foregroundColor: UIColor.red,
]
fullString.setAttributes(attributedtest, range: NSRange(location: 0, length: 5))
view.attributedText = fullString
print("\(fullString.attributedSubstring(from: NSRange(location: 0, length: 5)))")
return view
}
func updateUIView(_ uiView: UITextView, context: Context) {
}
}
class sizeFont: UIFont{
}
extension NSAttributedString.Key {
static let sizefont: NSAttributedString.Key = .init(rawValue:"sizeFont")
}
And Picture
Thanks
My only experience with NSAttributed text is for smooth conversion of a integer to a smoothly scaled image.
This is how I did it, perhaps it can help :
func imageOf(_ val: Int, backgroundColor: UIColor = .gray, foregroundColor: UIColor = .yellow) -> UIImage {
let t:String = (val==0) ? " " : String(val)
let attributes = [
NSAttributedString.Key.foregroundColor: foregroundColor,
NSAttributedString.Key.backgroundColor: backgroundColor,
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 70)
]
let textSize = t.size(withAttributes: attributes)
let renderer = UIGraphicsImageRenderer(size: textSize)
let newImage = renderer.image(actions: { _ in t.draw(at: CGPoint(x: 0, y:0), withAttributes: attributes) })
return newImage
}
Ask you this:
How iOS is supposed to know what effect to apply according to your new NSAttributedString.Key? How to render it?
By reading the only value (and not the key) and act according its type? If so, how could iOS know that an UIColor value is for NSAttributedString.Key.foregroundColor or for NSAttributedString.Key.backgroundColor.
In other words, you added information, but noone reads your mind and knows what to do with that.
You'd have to play with CoreText.framework, with useful infos there.
As seen, you can play more with a NSLayoutManager. For instance, at stome point drawUnderline(forGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:) will be called, and will render it.
Are you starting to see the logic, who will call that method with which params? Applied to your new key/value, which code will call the corresponding method?
I guess it should be in the NSAttributedString.Key.font, so you might have a method to convert it into the correct key. Maybe it's for a toggle (toggle between currently set font, and the value of sizeFont?), But according to the name, why not just put a CGFloat value?
You might ask a new question explaining what's the effect you want. But this answer should answer "why it's not working", or rather, "why it's not doing anything" (because working, I don't know what's supposed to do in the first place).
Hello I want to add ads to a swiftUI grid. The grid contains pictures that I get from a firebase backend and after every couple of pictures I would like to have an ad.
I am quite new to both SwiftUi and working with ads, so I'm not sure how correct my code is, but here is what I got so far.
// Code for the pictures Grid
struct PicturesGrid: View {
private let data: [Item]
var body: some View {
let gridItems = [GridItem(.fixed(UIScreen.screenWidth / 2),
alignment: .leading),
GridItem(.fixed(UIScreen.screenWidth / 2),
alignment: .leading)]
return ScrollView(showsIndicators: false) {
LazyVGrid(columns: gridItems) {
ForEach(0..<self.data.count, id: \.self) { index in
// Using this workaround for the ad to be on the whole width of the screen
// Also, after every six images I am adding and ad
if index != 0, index % 6 == 0 {
AdView()
.frame(width: UIScreen.screenWidth, height: 280)
.padding(.top, 20)
Spacer()
item
.frame(width: UIScreen.screenWidth / 2)
} else {
item
.frame(width: UIScreen.screenWidth / 2)
}
}
}
}
}
// this is for the picture
var item: some View {
NavigationLink(destination: DetailView(viewModel: DetailViewModel(item: itemAtIndexPath))) {
Cell(viewModel: CellViewModel(item: itemAtIndexPath))
}
.buttonStyle(PlainButtonStyle())
}
}
This is the code that I am currently using to load, create and display an ad
// Code for the ad that I am currently using
struct AdView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let adController = AdViewController(self)
return adController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
class AdViewController: UIViewController {
private var adView: AdView
/// The height constraint applied to the ad view, where necessary.
var heightConstraint: NSLayoutConstraint?
/// The ad loader. You must keep a strong reference to the GADAdLoader during the ad loading
/// process.
var adLoader: GADAdLoader!
/// The native ad view that is being presented.
var nativeAdView: GADUnifiedNativeAdView!
/// The ad unit ID.
let adUnitID = "ca-app-pub-3940256099942544/3986624511"
init(_ adView: AdView) {
self.adView = adView
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
var nibView: Any?
nibView = Bundle.main.loadNibNamed("ListAdView", owner: nil, options: nil)?.first
guard let nativeAdView = nibView as? GADUnifiedNativeAdView else {
return
}
setAdView(nativeAdView)
adLoader = GADAdLoader(adUnitID: adUnitID, rootViewController: self,
adTypes: [.unifiedNative], options: nil)
adLoader.delegate = self
DispatchQueue.global(qos: .background).async {
self.adLoader.load(GADRequest())
}
}
func setAdView(_ adView: GADUnifiedNativeAdView) {
// Remove the previous ad view.
DispatchQueue.main.async { [weak self] in
guard let weakSelf = self else {
return
}
weakSelf.nativeAdView = adView
weakSelf.view.addSubview(weakSelf.nativeAdView)
weakSelf.nativeAdView.translatesAutoresizingMaskIntoConstraints = false
// Layout constraints for positioning the native ad view to stretch the entire width and height
let viewDictionary = ["_nativeAdView": weakSelf.nativeAdView!]
weakSelf.view.addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "H:|[_nativeAdView]|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: viewDictionary)
)
weakSelf.view.addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "V:|[_nativeAdView]|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: viewDictionary)
)
}
}
}
extension AdViewController: GADUnifiedNativeAdLoaderDelegate {
func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error:
GADRequestError) {
print("didFailToReceiveAdWithError: \(error)")
}
func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADUnifiedNativeAd) {
print("Received unified native ad: \(nativeAd)")
// Deactivate the height constraint that was set when the previous video ad loaded.
heightConstraint?.isActive = false
// Populate the native ad view with the native ad assets.
// The headline and mediaContent are guaranteed to be present in every native ad.
(nativeAdView.headlineView as? UILabel)?.text = nativeAd.headline
nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent
// This app uses a fixed width for the GADMediaView and changes its height to match the aspect
// ratio of the media it displays.
if let mediaView = nativeAdView.mediaView, nativeAd.mediaContent.aspectRatio > 0 {
heightConstraint = NSLayoutConstraint(
item: mediaView,
attribute: .height,
relatedBy: .equal,
toItem: mediaView,
attribute: .width,
multiplier: CGFloat(1 / nativeAd.mediaContent.aspectRatio),
constant: 0)
heightConstraint?.isActive = true
}
// This asset is not guaranteed to be present. Check that it is before
// showing or hiding it.
(nativeAdView.advertiserView as? UILabel)?.text = nativeAd.advertiser
nativeAdView.advertiserView?.isHidden = nativeAd.advertiser == nil
// In order for the SDK to process touch events properly, user interaction should be disabled.
nativeAdView.callToActionView?.isUserInteractionEnabled = false
// Associate the native ad view with the native ad object. This is
// required to make the ad clickable.
// Note: this should always be done after populating the ad views.
nativeAdView.nativeAd = nativeAd
}
}
I want to mention that this is working at the moment, but the problems that I want to fix and I don't know how are:
The grid with the pictures load, but when I scroll over an ad, it takes several seconds for the ad to load and display. How could I at least hide it while it loads or make it faster?
If I scroll over an ad, the ad loads and if I continue scrolling, when I scroll back up, the ad is not loaded anymore and I have to wait for it to load again. How can I fix this? Or what is the best practice for this kind of scenario?
Should I use multipleAds? To load them before showing? If yes, then how should I do this?
Does what I am doing here look even a little bit correct? Please...I need some help
The Best Way to show ads in SwiftUI Grids is implementing Native Ads in your app to provide personalized ad experience
So I'm trying to implement a custom animation as my app transitions from one View Controller to another, but for some reason the animateTransition function in my custom animation class is never called.
For the record, I'm using Xcode 8 and writing in Swift 3. The problem I'm trying to over come, is that the function is never called - I'll sort out the actual animation in the future, for now its
Here is the code in my CustomPresentAnimationController class, which should handle the transition animation...
import UIKit
class CustomPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
let duration = 0.5
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
print("Checking duration")
return duration
}
func animationController(forPresented presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 1")
return self
}
func presentationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 2")
return self
}
func animationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("This ran 3")
return self
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
print("It's working!")
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from) else {
return
}
guard let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else {
return
}
let container = transitionContext.containerView
let screenOffDown = CGAffineTransform(translationX: 0, y: -container.frame.height)
let screenOffUp = CGAffineTransform(translationX: 0, y: container.frame.height)
container.addSubview(fromView)
container.addSubview(toView)
toView.transform = screenOffUp
UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: [], animations: {
fromView.transform = screenOffDown
fromView.alpha = 0.5
toView.transform = CGAffineTransform.identity
toView.alpha = 1
}) { (success) in
transitionContext.completeTransition(success)
}
}
}
Here is the code for my ViewController (which both of my View Controllers reference)...
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate {
override func viewDidLoad() {
if transitioningDelegate != nil {
print("Should do something...")
print(transitioningDelegate)
} else {
print("Transitioing Delegate set to nil")
}
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let customPresentAnimationController = CustomPresentAnimationController()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("doing our custom transition")
print(segue.destination)
let destination = segue.destination
destination.transitioningDelegate = customPresentAnimationController
}
}
When I run the code, and click on the button I provided, which links to my seance View Controller, and is set to 'Present Modally', the view changes with the standard transition (slides up from the bottom) - and the following is printed out to Xcode:
Transitioing Delegate set to nil
doing our custom transition
<moduleView.ViewController: 0x7fe427f09a40>
Should do something...
Optional(<moduleView.CustomPresentAnimationController: 0x60800002e980>)
Obviously the first line is just as the first view loads, all the rest shows that my transitionDelegate is set on the Segue destination, and is indeed loaded in as the second view loads, and that the transitionDelegate is set to CustomPresentAnimationController... yet none of the functions in that class are ever called as it never prints anything out from those functions.
Any help appreciated!
Ensure the method signature for implementing the delegate matches the updated Swift 3 syntax.