I've recently attempted to add a welcome screen to my AR app that works as a Home screen. When the app loads, the user can tap the button and then the app freezes, crashes and displays the code
"Thread 1: "-[_0_2_2020_2.WelcomeViewController letsGo:]: unrecognized selector sent to instance 0x13ec05e00"
I've tried a few of the solutions available, but I haven't been able to come up with a solution. I think it has something to do with my *IBAction connection. Any assistance is greatly appreciated!
import UIKit
import RealityKit
import ARKit
class WelcomeViewController: UIViewController {
#IBAction func gotPressed(_ sender: Any) {let storyboard = UIStoryboard(name: "Main",
bundle: nil)
if let viewController = storyboard.instantiateViewController(withIdentifier:
"ViewController") as? ViewController {
self.present(viewController, animated: true, completion: nil) /// present the view
controller (the one with the ARKit)!
} }
}
class ViewController: UIViewController, ARSessionDelegate {
//delay app launch to show splash screen
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 3.0)
// Override point for customization after application launch.
return true
}
//end splash screen delay
#IBOutlet var arView: ARView!
override func viewDidLoad() {
super.viewDidLoad()
arView.session.delegate = self
showModel()
overlayCoachingView()
setupARView()
arView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:))))
}
func showModel(){
let anchorEntity = AnchorEntity(plane: .horizontal, minimumBounds:[0.7, 0.7])
anchorEntity.scale = [0.2, 0.2, 0.2]
let entity = try! Entity.loadModel(named: "COW_ANIMATIONS")
entity.setParent(anchorEntity)
arView.scene.addAnchor(anchorEntity)
}
//Overlay coaching view "adjust iphone scan"
func overlayCoachingView () {
let coachingView = ARCoachingOverlayView(frame: CGRect(x: 0, y: 0, width: arView.frame.width, height: arView.frame.height))
coachingView.session = arView.session
coachingView.activatesAutomatically = true
coachingView.goal = .horizontalPlane
view.addSubview(coachingView)
}//end overlay
func setupARView(){
arView.automaticallyConfigureSession = false
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]
configuration.environmentTexturing = .automatic
arView.session.run(configuration)
}
//object placement
#objc
func handleTap(recognizer: UITapGestureRecognizer){
let location = recognizer.location(in:arView)
let results = arView.raycast(from: location, allowing: .estimatedPlane, alignment: .horizontal)
if let firstResult = results.first {
let brownCowAnchor = ARAnchor(name: "COW_ANIMATIONS", transform: firstResult.worldTransform)
arView.session.add(anchor: brownCowAnchor)
} else {
print("Object placement failed - couldn't find surface.")
//cow animations
//let robot = try! ModelEntity.load(named: "COW_ANIMATIONS")
let brownCowAnchor = AnchorEntity()
let blackCowAnchor = AnchorEntity()
//anchor.children.append(robot)
//arView.scene.anchors.append(anchor)
//robot.playAnimation(robot.availableAnimations[0].repeat(duration: .infinity),
//transitionDuration: 0.5,
//startsPaused: false)
//start cow animation
let brownCow = try! ModelEntity.load(named: "COW_ANIMATIONS")
let blackCow = try! ModelEntity.load(named: "Cow")
brownCow.position.x = -1.0
blackCow.position.x = 1.0
brownCowAnchor.position.z = -2.0
blackCowAnchor.position.z = -2.0
brownCow.setParent(brownCowAnchor)
blackCow.setParent(blackCowAnchor)
arView.scene.anchors.append(brownCowAnchor)
arView.scene.anchors.append(blackCowAnchor)
let cowAnimationResource = brownCow.availableAnimations[0]
let horseAnimationResource = blackCow.availableAnimations[0]
brownCow.playAnimation(cowAnimationResource.repeat(duration: .infinity),
transitionDuration: 1.25,
startsPaused: false)
blackCow.playAnimation(horseAnimationResource.repeat(duration: .infinity),
transitionDuration: 0.75,
startsPaused: false)
//end cow animations
}
}
func placeObject(named entityName: String, for anchor: ARAnchor) {
let entity = try! ModelEntity.loadModel(named: entityName)
entity.generateCollisionShapes(recursive: true)
arView.installGestures([.rotation, .translation], for: entity)
let anchorEntity = AnchorEntity(anchor: anchor)
anchorEntity.addChild(entity)
arView.scene.addAnchor(anchorEntity)
}
}
The button is triggering a function letsGo which doesn't appear anywhere on the WelcomeViewController you posted. Check interface builder and make sure that you've removed the old connection from the button. Should be the final tab.
Related
My agora app has a custom video source as ARView that I transfer using ARVideoKit. How can I implement switching to the front camera?
My initial idea was just to set local video, but it does nothing
#objc private func switchCamera() {
captureType = captureType == .ar ? .camera : .ar
setCaptureType(to: captureType)
}
private func stopScene(){
arRecorder.rest()
sceneView.session.pause()
}
private func startScene() {
sceneView.session.run(configuration)
arRecorder.prepare(configuration)
}
private func setCaptureType(to type: CaptureType) {
switch type {
case .ar:
startScene()
agoraKit.disableVideo()
agoraKit.setVideoSource(arVideoSource)
case .camera:
stopScene()
agoraKit.enableVideo()
agoraKit.setVideoSource(nil)
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = 0
videoCanvas.renderMode = .hidden
videoCanvas.view = localVideoView
agoraKit.setupLocalVideo(videoCanvas)
}}
Basically, I need to stop ARSession, probably remove the custom video source and set local video as input.
To set ARView as a video source I followed this tutorial
You don't need to switch the camera source for Agora, instead you should update the ARKit config to use the front camera
class ViewController: UIViewController, ARSCNViewDelegate, RenderARDelegate, RecordARDelegate {
weak var cameraFlipBtn : UIButton!
enum cameraFacing {
case front
case back
}
var activeCam: cameraFacing = .back
override func viewDidLoad() {
super.viewDidLoad()
// Configure ARKit Session
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]
self.activeCam = .back // set the active camera
// Reverse camera button
if ARFaceTrackingConfiguration.isSupported {
// add reverse camera button
let reverseCameraBtn = UIButton()
reverseCameraBtn.frame = CGRect(x: self.view.bounds.maxX-75, y: 25, width: 50, height: 50)
if let imageReverseCameraBtn = UIImage(named: "cameraFlip") {
reverseCameraBtn.setImage(imageReverseCameraBtn, for: .normal)
}
self.view.insertSubview(reverseCameraBtn, at: 3)
self.cameraFlipBtn = reverseCameraBtn
}
self.cameraFlipBtn.addTarget(self, action: #selector(switchCamera), for: .touchDown)
}
#objc private func switchCamera() {
if self.activeCam == .back {
// switch to front config
let configuration = ARFaceTrackingConfiguration()
configuration.isLightEstimationEnabled = true
// run the config to swap the camera
self.sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
self.activeCam = .front
} else {
// switch to back cam config
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]
// run the config to swap the camera
self.sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
self.activeCam = .back
}
}
}
instead of enableVideo()/disableVideo() video, try:
self.agoraKit.enableLocalVideo(true/false)
Hi there I am trying to recreate apple musics miniplayer controller. There is a view that shows details about the song playing such as the song name, artist name, cover art and so forth like apple music. When a user clicks the dismiss button on the top of that controller, it minimizes it to a view just above the tabBar revealing the rootview behind the view. The only problem is that my code is causing an issue that when the view is minimized, instead of minimizing the view that shows the information about the current song being played, it minimizes all the views and leaves just a black screen. I'm not sure what is causing the issue but I will provide the code for my tabBar controller which houses the code to minimize and maximize the view and then the other controller which calls the function created in the tabBar controller to minimze and maximize the view as well as screen shots of what is happening. Thank you for taking the time to look at this. If anything is unclear please let me know.
TabBarController Code:
import Foundation
import UIKit
import Firebase
class TabBarController: UITabBarController {
var user: User? {
didSet {
guard let nav = viewControllers?[0] as? UINavigationController else { return }
guard let feed = nav.viewControllers.first as? FeedController else { return }
feed.user = user
}
}
override func viewDidLoad() {
super.viewDidLoad()
fetchUser()
setupDetailsPlayerView()
// perform(#selector(minimizePlayerDetails), with: nil, afterDelay: 1)
// perform(#selector(maximizePlayerDetails), with: nil, afterDelay: 1)
}
#objc func minimizePlayerDetails() {
maximizeTopAnchorConstraint.isActive = false
minimizeTopAnchorConstraint.isActive = true
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
// self.view.layoutIfNeeded()
})
}
#objc func maximizePlayerDetails() {
maximizeTopAnchorConstraint.isActive = true
maximizeTopAnchorConstraint.constant = 0
minimizeTopAnchorConstraint.isActive = false
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
})
}
func fetchUser() {
guard let uid = Auth.auth().currentUser?.uid else { return }
UserService.shared.fetchUser(uid: uid) { user in
self.user = user
}
}
let playerDetailsView = PlayerDetailController.initFromNib()
var maximizeTopAnchorConstraint: NSLayoutConstraint!
var minimizeTopAnchorConstraint: NSLayoutConstraint!
fileprivate func setupDetailsPlayerView() {
print("setting up details view")
// view.addSubview(playerDetailsView)
view.insertSubview(playerDetailsView, belowSubview: tabBar)
playerDetailsView.translatesAutoresizingMaskIntoConstraints = false
maximizeTopAnchorConstraint = playerDetailsView.topAnchor.constraint(equalTo: view.topAnchor, constant: view.frame.height)
maximizeTopAnchorConstraint.isActive = true
minimizeTopAnchorConstraint = playerDetailsView.topAnchor.constraint(equalTo: tabBar.topAnchor, constant: -64)
// minimizeTopAnchorConstraint.isActive = true
playerDetailsView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
playerDetailsView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
playerDetailsView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
}
function being called in songcontroller:
#IBAction func dismissTapped(_ sender: Any) {
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let sceneDelegate = windowScene.delegate as? SceneDelegate
else {
return
}
let viewController = TabBarController()
sceneDelegate.window?.rootViewController = viewController
viewController.minimizePlayerDetails()
print("clicked")
self.removeFromSuperview()
}
Screenshots of what is happening:
Normal View:
Minimized View:
If you've created the TabBarController by storyboard then the empty initialization you've provided won't work. You need to instantiate the UIViewController from the storyboard. This seems to be the reason why you're getting an empty UITabBarController when setting the rootViewController. Modify your button action dismissTapped like this:
#IBAction func dismissTapped(_ sender: Any) {
//...
let viewController = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "TabBarController") as? TabBarController
sceneDelegate.window?.rootViewController = viewController
//...
}
I made a side menu with some controls and I can dismiss it when the user taps outside of the side menu or if he/she selects a row inside the side menu. Now I want to add a swipe gesture to the left so that the user can dismiss it that way too.
extension MenuViewController {
#objc func dismissControllerAnimated() {
dismiss(animated: true, completion: nil)
} }
class SlideinTransition: NSObject, UIViewControllerAnimatedTransitioning {
let menuViewController = MenuViewController()
var isPresenting = true
let dimmingView = UIView()
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else { return }
let containerView = transitionContext.containerView
let finalWidth = toViewController.view.bounds.width * 0.3
let finalHeight = toViewController.view.bounds.height
if isPresenting{
//adds a tap gesture to our dimming view
let tapGesture = UITapGestureRecognizer(target: toViewController, action: #selector(MenuViewController.dismissControllerAnimated))
dimmingView.addGestureRecognizer(tapGesture)
//adds the dimming view
dimmingView.backgroundColor = .black
dimmingView.alpha = 0.0
containerView.addSubview(dimmingView)
dimmingView.frame = containerView.bounds
//adds the menu view controller to our container
containerView.addSubview(toViewController.view)
//init frame off the screen
toViewController.view.frame = CGRect(x: -finalWidth, y: 0, width: finalWidth, height: finalHeight)
}
let transform = {
self.dimmingView.alpha = 0.5
toViewController.view.transform = CGAffineTransform(translationX: finalWidth, y: 0)
}
//applies a specific kind of transformation to our view
let identity = {
self.dimmingView.alpha = 0.0
fromViewController.view.transform = .identity
}
//animates the transition and cancels it when you click outside of the frame
let duration = transitionDuration(using: transitionContext)
let isCancelled = transitionContext.transitionWasCancelled
UIView.animate(withDuration: duration, animations: {
self.isPresenting ? transform() : identity()
}) { (_) in
transitionContext.completeTransition(!isCancelled)
if !self.isPresenting {
self.dimmingView.removeFromSuperview()
}
}
}
}
I instantiate my "MenuViewController" like this into my MainController
#IBAction func didTapMenu(_ sender: UIBarButtonItem) {
guard let menuViewController = storyboard?.instantiateViewController(withIdentifier: "MenuViewController") as? MenuViewController else { return }
menuViewController.didTapMenuType = { menuType in
self.transitionToNew(menuType)
}
menuViewController.modalPresentationStyle = .overCurrentContext
menuViewController.transitioningDelegate = self
present(menuViewController, animated: true)
}
How can I add a SwipeGestureRecognizer to my transition? I appreciate every input. Thanks in advance!
it's possible to add GestureRecognizer or set on touch to PDFAnnotation
func setDocumentAnnotation() {
let anotation:PDFAnnotation = PDFAnnotation(bounds: CGRect(x: pointX, y: pointY, width: pointW, height: pointH), forType: .highlight, withProperties: nil)
anotation.color = .yellow
anotation.endLineStyle = .circle
guard let page = pdfView.currentPage else {return}
page.addAnnotation(anotation)
}
#objc func annotationTapping(_ sender: UITapGestureRecognizer){
print("------- annotationTapping ------")
}
Hopefully this method will work for you..
NotificationCenter.default.addObserver(self, selector: #selector (titleTapped), name:Notification.Name.PDFViewAnnotationHit , object: nil)
#objc func titleTapped() {
-Provide the action you want---
}
It's not possible to add the gesture recognizer to the annotation itself. However you can add it to the PDFView and then check if the user tapped on an annotation. Here's a simple snippet:
let tappedPage = pdfView.page(for: tapLocation, nearest: false)
if let page = tappedPage {
let tapPageLocation = pdfView.convert(tapLocation, to: page)
let annotation = page.annotation(at: tapPageLocation)
}
I hope this helps.
I have recently released my game to the App Store. The iAd banner and medium ads are not showing up after almost a week now. All its showing isa blank space as in the following screenshots.
Can anyone check if there is anything wrong with my iAd code?
Before Release:
After Release:
import UIKit
import SpriteKit
import iAd
import Social
import GameKit
import StoreKit
class GameViewController: UIViewController, ADBannerViewDelegate, GKGameCenterControllerDelegate, ADInterstitialAdDelegate {
var scene: GameScene!
#IBOutlet var bannerView: ADBannerView!
var imageView = UIImageView()
var interAd = ADInterstitialAd()
var mediumRectAdView = ADBannerView(adType: ADAdType.MediumRectangle)
var interAdView = UIView()
var closeButton = UIButton(type: UIButtonType.System)
override func viewDidLoad() {
super.viewDidLoad()
let screenBounds: CGRect = UIScreen.mainScreen().bounds
bannerView = ADBannerView(frame:CGRectMake(0,0,50,screenBounds.width))
bannerView.center = CGPoint(x: screenBounds.width/2, y:screenBounds.height-bannerView.frame.size.height/2)
bannerView.delegate = self
bannerView.hidden = false//false //<<<<<<<<<<<<<<<<<<<<<<,
scene = GameScene(size:CGSize(width: 2048, height: 1536))
let skView = self.view as! SKView
scene.scaleMode = .AspectFill
skView.presentScene(scene)
skView.addSubview(bannerView)
if scene.productPurchased == true {
bannerView.hidden = true
}
firstScene = false
//self.interstitialPresentationPolicy = ADInterstitialPresentationPolicy.Manual
//Authenticate Game Center
NSNotificationCenter.defaultCenter().addObserver(self, selector:#selector(GameViewController.showAuthenticationViewController),name:PresentAuthenticationViewController, object: nil)
SGGameKit.sharedInstance.authenticateLocalPlayer()
// Share
NSNotificationCenter.defaultCenter().addObserver(self, selector: (#selector(GameViewController.displayShareSheet)), name:mySharePostKey, object: nil)
// Med iAD
NSNotificationCenter.defaultCenter().addObserver(self, selector: (#selector(GameViewController.loadMedAd)), name:myloadMedAdKey, object: nil)
// Interstitial iAD
NSNotificationCenter.defaultCenter().addObserver(self, selector: (#selector(GameViewController.loadInterAd)), name:myloadInterAdKey, object: nil)
// Hide Iad
NSNotificationCenter.defaultCenter().addObserver(self, selector: (#selector(GameViewController.hideAd)), name:myHideAdKey, object: nil)
}
func hideAd() {
print("Hiding Ad")
bannerView.hidden = true
}
func displayShareSheet() {
let initialText = "OMG! I got \(scene.score) points in Jumpox\n" + "https://itunes.apple.com/us/app/id1099659993)"
if let myImage = self.view?.pb_takeSnapshot() {
UIImageWriteToSavedPhotosAlbum(myImage, nil, nil, nil)
let activityViewController = UIActivityViewController(activityItems: [initialText,myImage as UIImage], applicationActivities: [])
activityViewController.excludedActivityTypes = [UIActivityTypeOpenInIBooks]
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone {
self.presentViewController(activityViewController, animated: true, completion: {})
}
else {
let popup: UIPopoverController = UIPopoverController(contentViewController:activityViewController)
popup.presentPopoverFromRect(CGRectMake(self.view.frame.size.width / 2, self.view.frame.size.height / 4, 0, 0), inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
}
}
func loadMedAd() {
print("Loading loadMedAd")
closeButton = UIButton(type: UIButtonType.System)
closeButton.frame = CGRectMake(5, 5, 30, 30)
closeButton.layer.cornerRadius = 10
closeButton.setTitle("X", forState: .Normal)
closeButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
closeButton.backgroundColor = UIColor.whiteColor()
closeButton.layer.borderColor = UIColor.blackColor().CGColor
closeButton.layer.borderWidth = 1
closeButton.addTarget(self, action: #selector(GameViewController.close), forControlEvents: UIControlEvents.TouchDown)
mediumRectAdView!.delegate = self
mediumRectAdView.center = self.view.center
mediumRectAdView.addSubview(closeButton)
interAdView = UIView(frame: self.view.frame)
self.view.addSubview(interAdView)
interAdView.addSubview(mediumRectAdView)
}
func loadInterAd() {
print("Loading loadInterAd")
interAd = ADInterstitialAd()
interAd.delegate = self
//if interAd.loaded {
closeButton.frame = CGRectMake(10, 10, 40, 40)
closeButton.layer.cornerRadius = 10
closeButton.setTitle("X", forState: .Normal)
closeButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
closeButton.backgroundColor = UIColor.whiteColor()
closeButton.layer.borderColor = UIColor.blackColor().CGColor
closeButton.layer.borderWidth = 1
closeButton.addTarget(self, action: #selector(GameViewController.close), forControlEvents: UIControlEvents.TouchDown)
interAdView = UIView(frame: self.view.frame)
interAdView.frame = self.view.bounds
self.view.addSubview(interAdView)
interAd.presentInView(interAdView)
interAdView.addSubview(closeButton)
//}
//requestInterstitialAdPresentation()
}
func interstitialAd(interAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("Failed To Receive")
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
func close() {
closeButton.removeFromSuperview()
interAdView.removeFromSuperview()
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showAuthenticationViewController() {
let gameKitHelper = SGGameKit.sharedInstance
if let authenticationViewController = gameKitHelper.authenticationViewController {
self.presentViewController(authenticationViewController, animated: true,
completion: nil)
}
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController){
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
iAd has been discontinued and is not accepting new apps into the network.
The iAd App Network will be discontinued as of June 30, 2016. Although
we are no longer accepting new apps into the network, advertising
campaigns may continue to run and you can still earn advertising
revenue until June 30. If you’d like to continue promoting your apps
through iAd until then, you can create a campaign using iAd Workbench.
I'm surprised the reviewer did not reject your submission. Look into AdMob, AppLovin, or the many other ad networks available for iOS.