I'm working to implement a banner ad in the scene, but it always reports "Thread 1: EXC_BREAKPOINT(code=EXC_ARM_BREAKPOINT, subcode=Oxdefe) and the program stops running.
I referenced Mr. T's answer in another question about iAd("Swift - ADBannerView") but still couldn't make it.
The code looks like this:
import UIKit
import SpriteKit
import iAd
class GameViewController: UIViewController, ADBannerViewDelegate {
#IBOutlet var adBannerView: ADBannerView
override func viewDidLoad() {
super.viewDidLoad()
println("view loaded")
//iAd
self.canDisplayBannerAds = true
self.adBannerView.delegate = self
self.adBannerView.alpha = 0.0
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
//iAd
func bannerViewWillLoadAd(banner: ADBannerView!) {
println("sort of working1")
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
self.adBannerView.alpha = 1.0
println("sort of working2")
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
println("sort of working3")
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
println("sort of working4")
return true
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
}
}
And I created an ADBannerView in the Main.storyboard and linked it with the #IBOutlet adBannerView.
Anyone helps me figure out?
This is how I did it, possibly not all of it is necessary.
I did not use the banner in the Storyboard, so the IBOutlet is not necessary.
Also, if you manually create a banner, you do not need to set self.canDisplayBannerAds
This function (ported from ObjC) is how I display the ads.
func loadAds(){
adBannerView = ADBannerView(frame: CGRect.zeroRect)
adBannerView.center = CGPoint(x: adBannerView.center.x, y: view.bounds.size.height - adBannerView.frame.size.height / 2)
adBannerView.delegate = self
adBannerView.hidden = true
view.addSubview(adBannerView)
}
This is called in viewDidLoad. Then, in the didLoadAd delegate method, I set adBannerView.hidden = false and in didFailToReceiveAdWithError, adBannerView.hidden = true
I think hidden is better than alpha in this situation, as it feels more natural. I believe (but am not sure) that when hidden, the view is not drawn at all by the GPU, while with an alpha of 0, it is still drawn, but made invisible (correct me if I am wrong).
This is my setup, and it worked for me, so hopefully it will work in your case too!
Related
I am trying to implement drop delegates on a NSCollectionViewController and having issues using a custom NSCollectionViewItem with an additional View Layer I've added onto the CollectionView Item. FWIW, The additional view is used draw a dashed border to indicate a drop area.
The drag event works fine on this collectionItem, and all other collectionItems without this view when it is hidden, but as soon as the drag event occurs on top of this view, the drag event pauses.
The drag event resumes as soon as the mouse is dragged outside of the view, but nothing happens if I release the drag while the mouse is over the view.
I would love to know what is happening here and how to prevent the custom view from "stealing" the mouse event from the CollectionViewContoller.
Delegate Method on DropViewController
func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
print("1")
if proposedDropIndexPath.pointee.item <= self.destinationDirectoryArray.count {
if proposedDropOperation.pointee == NSCollectionView.DropOperation.on {
return .move
}
} else if proposedDropIndexPath.pointee.item == self.destinationDirectoryArray.count {
//There's some stuff here validating the URL removed for brevity. It works okay when the focus is outside the view, but happy to add back in if helpful
if proposedDropOperation.pointee == NSCollectionView.DropOperation.on {
return .move
}
}
return[]
}
Configuring Collection View
func configureCollectionView() {
let flowLayout = NSCollectionViewFlowLayout()
flowLayout.minimumInteritemSpacing = 8.0
flowLayout.minimumLineSpacing = 8.0
destinationCollectionView.delegate = self
destinationCollectionView.dataSource = self
destinationCollectionView.register(NSNib(nibNamed: "DestinationCollectionItem", bundle: nil), forItemWithIdentifier: directoryItemIdentifier)
destinationCollectionView.collectionViewLayout = flowLayout
destinationCollectionView.registerForDraggedTypes([.fileURL])
destinationCollectionView.setDraggingSourceOperationMask(NSDragOperation.move, forLocal: true)
}
Collection View Item Setup
class DestinationCollectionItem: NSCollectionViewItem {
#IBOutlet weak var backgroundLayer: NSView!
override func viewDidLoad() {
super.viewDidLoad()
self.highlightState = .none
view.wantsLayer = true
view.layer?.cornerRadius = 8.0
backgroundLayer.isHidden = true
}
}
Custom Border View - Applied custom class in Xib and linked to File's Owner
class BorderedView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let path : NSBezierPath = NSBezierPath(roundedRect: self.bounds, xRadius: 10.0, yRadius: 10.0)
path.addClip()
let dashHeight: CGFloat = 2
let dashLength: CGFloat = 7
let dashColor: NSColor = .lightGray
// setup the context
let currentContext = NSGraphicsContext.current!.cgContext
currentContext.setLineWidth(dashHeight)
currentContext.setLineDash(phase: 0, lengths: [dashLength])
currentContext.setStrokeColor(dashColor.cgColor)
// draw the dashed path
let cgPath : CGPath = CGPath(roundedRect: NSRectToCGRect(self.bounds), cornerWidth: 10.0, cornerHeight: 10.0, transform: nil)
currentContext.addPath(cgPath)
currentContext.strokePath()
}
}
Well - I solved this one pretty quick.
While I previously tried adding unregisterDraggedTypes() to the backgroundLayer, the issue turned out to also be occurring on the image layer. I applied it to both the Image and backgroundLayer and it works now.
Collection View Item Setup
class DestinationCollectionItem: NSCollectionViewItem {
#IBOutlet weak var backgroundLayer: NSView!
override func viewDidLoad() {
super.viewDidLoad()
self.highlightState = .none
view.wantsLayer = true
view.layer?.cornerRadius = 8.0
backgroundLayer.isHidden = true
backgroundLayer.unregisterDraggedTypes()
self.imageView?.unregisterDraggedTypes()
self.textField?.unregisterDraggedTypes()
}
}
I have been following this tutorial. I downloaded the source and tried "translating" it to Swift. This is my "translated" code:
import Cocoa
import AppKit
import MetalKit
import simd
class MetalViewController: NSViewController {
#IBOutlet var inview: MTKView!
override func viewDidLoad() {
super.viewDidLoad()
let _view: MTKView = self.inview
_view.device = MTLCreateSystemDefaultDevice()
let _renderer: Renderer=initView(view: _view)
_view.delegate=_renderer as? MTKViewDelegate
_view.preferredFramesPerSecond=60
}
}
class Renderer: NSObject {
init(device: MTLDevice){
self._device=device
self._commandQueue=_device.makeCommandQueue()!
super.init()
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
let color = Color(red: 1.0,green: 0.0,blue: 0.0,alpha: 0.0)
view.clearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha)
let commandbuffer = _commandQueue.makeCommandBuffer()
let renderpassdescriptor: MTLRenderPassDescriptor = view.currentRenderPassDescriptor!
let renderencoder: MTLRenderCommandEncoder = (commandbuffer?.makeRenderCommandEncoder(descriptor: renderpassdescriptor))!
renderencoder.endEncoding()
commandbuffer!.present(view.currentDrawable!)
commandbuffer!.commit()
}
var _device: MTLDevice
var _commandQueue: MTLCommandQueue
}
struct Color{
var red, green, blue, alpha: Double
}
func initView(view: MTKView) -> Renderer{
var renderer: Renderer
renderer=Renderer(device: view.device!)
return renderer
}
So I put the AAPLRenderer and AAPLViewControllers into one file, and made it so that there are no header files. I linked the view with #IBOutlet to the view controller because the view was a NSView and I cannot cast it to MTKView without getting a compile time error. The AppDelegate is the original one and I do not have a main file.
I end up with a window that does not show red, but rather shows nothing. I do not understand why this is happening. Please help me, thank you.
I see two issues.
1) MTKView's delegate property is a weak var, which means that if you don't hold onto an instance of your renderer, it'll be immediately deinited and never receive any delegate callbacks. Keep a reference to your renderer as a property on your view controller.
class MetalViewController: NSViewController {
#IBOutlet var inview: MTKView!
var renderer: Renderer!
override func viewDidLoad() {
// ...
let view: MTKView = self.inview
// ...
renderer = initView(view: view)
view.delegate = renderer
// ...
}
}
2) Because the Renderer class doesn't explicitly declare conformance to the MTKViewDelegate protocol, the conditional cast when assigning it as the view's delegate fails. Make Renderer explicitly conform to the protocol, and remove the conditional cast as shown above.
class Renderer: NSObject, MTKViewDelegate
Well, it could be anything. But, the first thing I would check is that your alpha setting for that red color should have alpha = 1.0 and not alpha = 0.0.
So i have created a couple of different SKScenes in my swift project and I've seen on a couple tutorials that you don't have to launch you gameScene.swift as your first scene, however i can not find a tutorial that explains how
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve
rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
}
Im trying to change the GameScene SKS to my MainMenu SKS, but i keep getting errors and I'm definitely missing something. Can anyone help me out?
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let newScene = MenuScene(fileNamed:"MenuScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve
rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
newScene.scaleMode = .AspectFill
skView.presentScene(newScene)
}
}
}
So instead of having GameScene as your initial scene, just create MenuScene instance and present it in your viewDidLoad. Note that you have to create coresponding MenuScene.sks file because of this line:
If let newScene = MenuScene(fileNamed:"MenuScene.sks"){
//...}
because you are loading it from a file.
Also to transition to another scene, use following pattern and for example in touchesBegan create new scene, optionally create SKTransition and present the new scene.
Just call that code whenever you want. For example, you can connect it with a button.
#IBAction func playGame(sender: AnyObject) {
if let scene = GameScene(fileNamed:"GameScene") {
...
}
}
this is what I used in a Sprite Kit project. It was I while ago and I dont remember any of the reasoning behind it.
if let scene = DesiredScene.unarchiveFromFile("DesiredScene") as? DesiredScene
perhaps someone else can elucidate whats going on
I have a class called IntroLayer I'm trying to load as the inital scene when my game is launched. However after simply changing the GameScene to IntroScene as described in these simple steps, my IntroScene isn't being loaded. I set breakpoints on if let scene to see that it skips over it, and even set breakpoints in the actual IntroScene to verify didMoveToView is not being called. Any ideas?
I changed the let scene from GameScene to IntroScene in the GameViewController like so:
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = IntroScene(fileNamed:"IntroScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .AllButUpsideDown
} else {
return .All
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
Then renamed GameScene.swift to IntroScene.swift and changed the class to this:
import SpriteKit
class IntroScene: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = UIColor.blackColor()
let fadeIn:SKAction = SKAction.fadeInWithDuration(1.0)
let fadeOut:SKAction = SKAction.fadeOutWithDuration(1.0)
let inspiredText = SKLabelNode(fontNamed: "Dead Kansas")
inspiredText.alpha = 0.0
inspiredText.text = "Inspired By"
inspiredText.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
addChild(inspiredText)
inspiredText.runAction(fadeIn)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
// let location = touch.locationInNode(self)
}
}
}
However when I launch the app, it doesn't appear that IntroScene is even called when I set breakpoints, and I'm getting a screen with a gray background.
You need to rename your GameScene.sks to IntroScene.sks
The sks file is an archived file for your scene data, if you open it up, you will notice you can change a lot of things about the scene, and even add nodes and sprites directly to it. When you call if let scene = IntroScene(fileNamed:"IntroScene") { you are unarchiving the sks file to be loaded as a scene, so anytime you want to create a new scene, remember that you will need this file.
Trying to recognize a right click on a NSStatusItem I got a suggestion ( Thanks to Zoff Dino ) to use a NSClickGestureRecognizer for that. But for some bizarre reason it isn't working as it should be. I am able to recognize a left click (buttonMask = 0x1) but not a right-click (buttonMask = 0x2). This is how I would like it to work but it isn't:
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
if let button = statusItem.button {
// Add right click functionality
let gesture = NSClickGestureRecognizer()
gesture.buttonMask = 0x2 // right mouse
gesture.target = self
gesture.action = "rightClickAction:"
button.addGestureRecognizer(gesture)
}}
func rightClickAction(sender: NSGestureRecognizer) {
if let button = sender.view as? NSButton {
NSLog("rightClick")
}
}
UPDATE:
I still did not manage to gets to work. Somehow it doesn't react on a right click (but changing the code on a left click) does. I guess some really simple issues are occurring that seem to block it from working. Even stranger is the fact that gesture.buttonMask = 0x1 works on the left click.
An alternative solution rather than NSClickGestureRecognizer is to attach a custom view to the status bar and handle the event from there.
The small disadvantage is you have to take care of the drawing and menu delegate methods.
Here a simple example:
Create a file StatusItemView a subclass of NSView
import Cocoa
class StatusItemView: NSView, NSMenuDelegate {
//MARK: - Variables
weak var statusItem : NSStatusItem!
var menuVisible = false
var image : NSImage! {
didSet {
if image != nil {
statusItem.length = image.size.width
needsDisplay = true
}
}
}
//MARK: - Override functions
override func mouseDown(theEvent: NSEvent) {
if let hasMenu = menu {
hasMenu.delegate = self
statusItem.popUpStatusItemMenu(hasMenu)
needsDisplay = true
}
}
override func rightMouseDown(theEvent: NSEvent) {
Swift.print(theEvent)
}
//MARK: - NSMenuDelegate
func menuWillOpen(menu: NSMenu) {
menuVisible = true
needsDisplay = true
}
func menuDidClose(menu: NSMenu) {
menuVisible = false
menu.delegate = nil
needsDisplay = true
}
//MARK: - DrawRect
override func drawRect(dirtyRect: NSRect) {
statusItem.drawStatusBarBackgroundInRect(bounds, withHighlight:menuVisible)
let origin = NSMakePoint(2.0, 3.0) // adjust origin if necessary
image?.drawAtPoint(origin, fromRect: dirtyRect, operation: .CompositeSourceOver, fraction: 1.0)
}
}
In AppDelegate you need a reference to the custom menu and an instance variable for the NSStatusItem instance
#IBOutlet weak var menu : NSMenu!
var statusItem : NSStatusItem!
In applicationDidFinishLaunching create the view and attach it to the status item. Be aware to set the image of the view after attaching it to make sure the width is considered.
func applicationDidFinishLaunching(aNotification: NSNotification) {
statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1) // NSVariableStatusItemLength)
let statusItemView = StatusItemView(frame: NSRect(x: 0.0, y: 0.0, width: statusItem.length, height: 22.0))
statusItemView.statusItem = statusItem;
statusItemView.menu = menu
statusItem.view = statusItemView
statusItemView.image = NSImage(named: NSImageNameStatusAvailable)
}
The special case control-click to trigger the right-click function is not implemented.