how to add new SKScene With in SKScene in SpritKit Swift - objective-c

I have to add next SKScene in SpritKit in swift
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!)
{
var objgamescen = GameScene()
//objgamescen.initWithSize(self.size, playerWon: true)
self.view .presentScene(objgamescen)
}
Then GameScene is Blanked and Self.frame is (0.0,0.0,1.0,1.0)

Multiple Scene Added in SKScene By This:
let skView = self.view as SKView
skView.showsFPS = false
skView.showsNodeCount = false
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
var scene: HomeScene!
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
if UIScreen.mainScreen().bounds.size.width < 568 {
scene = HomeScene(size: CGSizeMake(480, 320))
}else {
scene = HomeScene(size: CGSizeMake(568, 320))
}
}else {
scene = HomeScene(size: CGSizeMake(1024, 768))
}
scene.scaleMode = .AspectFill
skView.presentScene(scene)

It sounds like you are just trying to restart the scene, you can do that by creating a function like this
func goToGameScene(){
let gameScene:GameScene = GameScene(size: self.view!.bounds.size) // create your new scene
let transition = SKTransition.fadeWithDuration(1.0) // create type of transition (you can check in documentation for more transtions)
gameScene.scaleMode = SKSceneScaleMode.Fill
self.view!.presentScene(gameScene, transition: transition)
}
Then you just call it where you want it to take place
goToGameScene()

Related

Problems using long hold & single tap in game together

I'm making a game with two methods of moving. When tapping the screen the player moves up. The game world also moves towards the player looking like the bird is flying forward. When the user holds down the on the screen the player will start flying forward in its position. When the player doesn't touch the screen at all, the player falls (almost like flappy bird).
The game worked fine and smooth until I added the long press method. Because one second is too short, it gets detected as a tap. So when I tap the screen fast 2 times the long press method gets called as well. So I'm thinking there must be another way instead of using long press gesture?
Is there any way to programmatically detect touch up inside and touch and hold in spritekit? I know you could do this in UIKit singleview applications on buttons.
But I need to make it work anywhere on the screen and not just from tapping a button.
I'm using Spritekit as game technology and Xcode as Platform.
Any ideas?
Here is a full working solution for you. Tap to jump the block, hold the screen to move forward. when you touch a wall you will reset position.
Swift3:
class Bird: SKSpriteNode {
private let flyUpImpulse = CGVector(dx: 0, dy: 20)
private let flyForwardForce = CGVector(dx: 100, dy: 0)
// var pb: SKPhysicsBody { return self.physicsBody! }
func initialize() {
color = .blue
size = CGSize(width: 50, height: 50)
physicsBody = SKPhysicsBody(rectangleOf: size)
physicsBody!.categoryBitMask = UInt32(1)
}
func flyUp() {
physicsBody!.applyImpulse(flyUpImpulse)
}
func flyForward() {
physicsBody!.applyForce(flyForwardForce)
}
func resetPosition() {
guard let scene = self.scene else {
print("reset position failed: bird has not been added to scene yet!")
return
}
run(.move(to: CGPoint(x: scene.frame.minX + 50, y: scene.frame.midY), duration: 0))
physicsBody!.velocity = CGVector.zero
}
}
class GameScene: SKScene, SKPhysicsContactDelegate {
enum ControlToDo { case tap, longPress, none }
var controlToDo = ControlToDo.none
// How many frames we have to release screen in order to recognize a "tap".
// Less frames will give a faster response time, but may also give incorrect input:
let tapThreshold = 10
var isTouchingScreen = false
var frameCountSinceFirstTouch = 0
let bird = Bird()
// Scene setup:
override func didMove(to view: SKView) {
removeAllChildren() // Remove this from your actual project.
anchorPoint = CGPoint(x: 0.5, y: 0.5)
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
physicsBody!.categoryBitMask = UInt32(2)
physicsBody!.contactTestBitMask = UInt32(1)
physicsWorld.gravity = CGVector(dx: 0, dy: -2)
physicsWorld.contactDelegate = self
bird.initialize()
addChild(bird)
}
// Touch handling stuff:
func tap() {
bird.flyUp()
controlToDo = .none
}
func longPress() {
bird.flyForward()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouchingScreen = true
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
isTouchingScreen = false
if frameCountSinceFirstTouch > tapThreshold {
controlToDo = .none
frameCountSinceFirstTouch = 0
}
else if frameCountSinceFirstTouch < tapThreshold {
controlToDo = .tap
frameCountSinceFirstTouch = 0
}
}
override func update(_ currentTime: TimeInterval) {
// Increase counter if touching the screen:
if isTouchingScreen {
frameCountSinceFirstTouch += 1
}
// If we have held the screen for long enough, do a longPress:
if frameCountSinceFirstTouch > tapThreshold {
controlToDo = .longPress
}
switch controlToDo {
case .tap: tap()
case .longPress: longPress()
case .none: break
}
}
// Reset positon on touch of wall:
func didBegin(_ contact: SKPhysicsContact) {
bird.resetPosition()
}
}
Objective C:
// MAKE SURE YOU HAVE <SKPhysicsContactDelegate> in your .h file!
#import "GameScene.h"
#implementation GameScene {
SKSpriteNode *_bird;
/// 1 is tap, 2 is longpress (i couldn't figure out how to compare strings, or do an enum)
int _controlToDo;
CGFloat _tapThreshold;
CGFloat _frameCountSinceFirstTouch;
Boolean _isTouchingScreen;
}
- (void) setupProperties {
_controlToDo = 0;
_tapThreshold = 10;
_isTouchingScreen = false;
_frameCountSinceFirstTouch = 0;
}
- (void) setupScene {
self.anchorPoint = CGPointMake(0.5, 0.5);
[self removeAllChildren];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = 2;
self.physicsBody.contactTestBitMask = 1;
self.physicsWorld.gravity = CGVectorMake(0, -2);
self.physicsWorld.contactDelegate = self;
_bird = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(50, 50)];
_bird.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_bird.size];
_bird.physicsBody.categoryBitMask = 1;
[self addChild:_bird];
}
- (void) birdResetPosition {
CGFloat min = 0 - (self.size.width / 2) + (_bird.size.width / 2) + 5;
CGPoint center = CGPointMake(min, 0);
SKAction *movement = [SKAction moveTo:center duration:0];
[_bird runAction:movement];
}
- (void)didMoveToView:(SKView *)view {
[self setupProperties];
[self setupScene];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_isTouchingScreen = true;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
_isTouchingScreen = false;
if (_frameCountSinceFirstTouch > _tapThreshold) {
_controlToDo = 0;
}
else if (_frameCountSinceFirstTouch < _tapThreshold) {
_controlToDo = 1;
}
_frameCountSinceFirstTouch = 0;
}
-(void)update:(CFTimeInterval)currentTime {
// Increase counter if touching the screen:
if (_isTouchingScreen == true) {
_frameCountSinceFirstTouch += 1;
}
// If we have held the screen for long enough, do a longPress:
if (_frameCountSinceFirstTouch > _tapThreshold) {
_controlToDo = 2;
}
// Bird fly up:
if (_controlToDo == 1) {
[_bird.physicsBody applyImpulse:CGVectorMake(0, 20)];
_controlToDo = 0;
}
// Bird fly forward:
else if (_controlToDo == 2) {
[_bird.physicsBody applyForce:CGVectorMake(100, 0)];
}
}
- (void) didBeginContact:(SKPhysicsContact *)contact {
[self birdResetPosition];
_bird.physicsBody.velocity = CGVectorMake(0, 0);
}
#end
All SKNodes have access to touch events, you just need to enable userInteractionEnabled
By default, this is set to false, but if you are using an SKS file, then the SKS default is set to true
As long as you do not have any other node enabled, when you touch the scene, it will fire for the scene.
To get a hold event going, I would recommend using SKAction's on your scene.
Basically, we want to wait for a specific time period, then fire the event
If at any point the finger is removed, then we remove the action, not firing your event.
Swift:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let wait = SKAction.wait(forDuration:1)
let beginHoldEvent = SKAction(run:{//function to start hold event})
run(SKAction.sequence([wait,beginHoldEvent],withKey:"holding")
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
guard let _ = action(forKey:"holding") else {return}
removeAction(forKey:"holding")
//do single click event
}
Objective C:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
void (^holdBlock)(void) = ^{
//do holding code here
};
SKAction* wait = [SKAction waitForDuration:1];
SKAction* beginHoldEvent = [SKAction runBlock:holdBlock];
SKAction* seq = [SKAction sequence:#[wait,beghinHoldEvent]];
[self runAction:seq withKey:#"holding"];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if([self actionForKey:#"holding"] == nil){
//we are on a long touch do nothing
return;
}
[self removeActionForKey:#"holding")];
//do tap event
}
Now this code does not handle multiple touches, you need to handle that.

How can I create a button with a background color for tvOS while still showing focus?

All I want to do is add a background color to a button for all states. But I want to maintain the automatic focus shadow that you get "for free" when using a system button in the tvOS storyboard. So far I haven't been able to find a combination that allows this.
Alternatively, I would also be interested in a way to programmatically add the shadow when the button is focused, but short of subclassing the button (which I haven't yet tried), I don't know how to do that either.
You can add a shadow for your custom button like this:
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
context.nextFocusedView.layer.shadowOffset = CGSizeMake(0, 10);
context.nextFocusedView.layer.shadowOpacity = 0.6;
context.nextFocusedView.layer.shadowRadius = 15;
context.nextFocusedView.layer.shadowColor = [UIColor blackColor].CGColor;
context.previouslyFocusedView.layer.shadowOpacity = 0;
}
Wasn't happy with a simple colour change, so I made a custom button subclass to look more like the default animation you get with system buttons -
class CustomButton: UIButton
{
private var initialBackgroundColour: UIColor!
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
initialBackgroundColour = backgroundColor
}
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
coordinator.addCoordinatedAnimations(
{
if self.focused
{
self.backgroundColor = UIColor.whiteColor()
UIView.animateWithDuration(0.2, animations:
{
self.transform = CGAffineTransformMakeScale(1.1, 1.1)
},
completion:
{
finished in
UIView.animateWithDuration(0.2, animations:
{
self.transform = CGAffineTransformIdentity
},
completion: nil)
})
}
else
{
self.backgroundColor = self.initialBackgroundColour
}
},
completion: nil)
}
}
Nothing too complicated, but gets the job done
Override didUpdateFocusInContext method and check if next focus view is button, if yes then customize its UI, and to set it back to orignal state check context.previousFocusedView was that button, something like below
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if (context.nextFocusedView == _button)
{
// set background color
}
else if (context.previousFocusedView == _button)
{
// set background color to background
}
}
The Ultimate Solution with inspiration from SomaMan. Just subclass all your custom buttons and you Get this:
includes: on tap animation and release and drag away.
//
// CustomFocusButton.swift
//
import UIKit
class CustomFocusButton: UIButton {
let focusedScaleFactor : CGFloat = 1.2
let focusedShadowRadius : CGFloat = 10
let focusedShadowOpacity : Float = 0.25
let shadowColor = UIColor.blackColor().CGColor
let shadowOffSetFocused = CGSizeMake(0, 27)
let animationDuration = 0.2
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
coordinator.addCoordinatedAnimations({
if self.focused{
UIView.animateWithDuration(self.animationDuration, animations:{ [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.clipsToBounds = false
weakSelf.layer.shadowOpacity = weakSelf.focusedShadowOpacity
weakSelf.layer.shadowRadius = weakSelf.focusedShadowRadius
weakSelf.layer.shadowColor = weakSelf.shadowColor
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
},completion:{ [weak self] finished in
guard let weakSelf = self else {return}
if !finished{
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.clipsToBounds = false
weakSelf.layer.shadowOpacity = weakSelf.focusedShadowOpacity
weakSelf.layer.shadowRadius = weakSelf.focusedShadowRadius
weakSelf.layer.shadowColor = weakSelf.shadowColor
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
}
})
} else {
UIView.animateWithDuration(self.animationDuration, animations:{ [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.clipsToBounds = true
weakSelf.transform = CGAffineTransformIdentity
}, completion: {[weak self] finished in
guard let weakSelf = self else {return}
if !finished{
weakSelf.clipsToBounds = true
weakSelf.transform = CGAffineTransformIdentity
}
})
}
}, completion: nil)
}
override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
UIView.animateWithDuration(animationDuration, animations: { [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformIdentity
weakSelf.layer.shadowOffset = CGSizeMake(0, 10);
})
}
override func pressesCancelled(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
if focused{
UIView.animateWithDuration(animationDuration, animations: { [weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
})
}
}
override func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
if focused{
UIView.animateWithDuration(animationDuration, animations: {[weak self] () -> Void in
guard let weakSelf = self else {return}
weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
})
}
}
}
I've found something better:
-(void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
[coordinator addCoordinatedAnimations:^{
if (self.focused) {
self.backgroundColor = [UIColor whiteColor];
}
else {
self.backgroundColor = [UIColor clearColor];
}
} completion:nil];
}
Swift 4 /tvOS11 and better:
Set the ButtonType in the Interface Builder button properties to "Plain".
Add this private extension to your class:
private extension UIImage {
static func imageWithColor(color: UIColor, size: CGSize) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Then in your connected IBOutlet set the background image for the focused state of the button:
#IBOutlet weak var myButton: UIButton! {
didSet {
let backgroundImageSelected = UIImage.imageWithColor(color: .red, size: myButton.bounds.size)
myButton.setBackgroundImage(backgroundImageSelected, for: .focused)
}
}
You can use the UIButton method setBackgroundImage(image: UIImage?, forState state: UIControlState) and pass through an image that is a flat color and the state .Normal.
This image can easily be created programatically from a UIColor and a size of 1x1:
func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
let rect = CGRectMake(0, 0, size.width, size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
You can set the background image in storyboard to an image that contains the color you would like

XIB View background is set to Clear Color, but it is not transparent

I need set backgroud color in title to transparent, but it is not set to transparent, i am using custom dialog pin in MAPKit. whats is wrong ?
Backgroud View color is set to CLear Color..
And programatically is set color to Clear Color:
customMarkerXIB.backgroundColor = UIColor.clearColor()
import UIKit
#IBDesignable class Widget: UIView {
var view: UIView!
var nibName: String = "Widget"
#IBOutlet weak var titleLabel: UILabel!
/* #IBInspectable var image: UIImage? {
get {
return imageView.image
}
set(image) {
imageView.image = image
}
}
*/
#IBInspectable var title: String? {
get {
return titleLabel.text
}
set(title) {
titleLabel.text = title
}
}
func setSelectedState(){
println("hola")
}
// init
override init(frame: CGRect) {
// properties
super.init(frame: frame)
// Set anything that uses the view or visible bounds
setup()
}
required init(coder aDecoder: NSCoder) {
// properties
super.init(coder: aDecoder)
// Setup
setup()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as UIView
return view
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
Loading custom annotation
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
var identifier = "CustomAnnotation"
var position = 0;
if let mapPin = annotation as? MyAnnotation {
position = mapPin.position; // assosiate annotation with a position
}
if annotation.isKindOfClass(MyAnnotation) {
var marker = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if marker == nil {
marker = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
marker.tag = position;
marker.image = UIImage(named: "mapa_pin")
marker.centerOffset = CGPointMake(0, -10)
marker.canShowCallout = true
marker.backgroundColor = UIColor.clearColor()
//var pointTitle = marker!.annotation.title! as String
/* var label:UILabel = UILabel(frame: CGRectMake(0, CGFloat(floorf((56.0-20)/2)), 26.0, 20))
label.backgroundColor = UIColor.clearColor()
label.text = "asdfasdfasdfasd"
label.textAlignment = NSTextAlignment.Center
label.font = UIFont.systemFontOfSize(11.0)
marker.addSubview(label) */
var customMarkerXIB : Widget = Widget(frame: CGRect(x: 0, y: 250, width: 300, height: 150))
customMarkerXIB.backgroundColor = UIColor.clearColor()
customMarkerXIB.title = "olakease";
customMarkerXIB.setSelectedState()
marker.rightCalloutAccessoryView = customMarkerXIB
// Callout
/* var button = UIButton.buttonWithType(.DetailDisclosure) as UIButton
marker!.leftCalloutAccessoryView = button
var image = UIImageView(image: UIImage(named: "mapa_pin"))
marker!.rightCalloutAccessoryView = image*/
} else {
marker!.annotation = annotation
}
return marker
}

MapKit : add MKPolyline hide MKTileOverlay

I have a MKMapView working with a MKTileOverlay showing tiles from a local database. It's working fine.
Then I used MKDirections to get direction between two coordinates and draw the route like that :
MKRoute *route = response.routes.lastObject;
MKPolyline *polyline = route.polyline;
// Draw path on overlay
[self.mapView insertOverlay:polyline aboveOverlay:self.tileOverlay];
But when I zoom to see the line, it appears without the tile background (normaly loaded from MKTileOverlay (stored into self.tileOverlay)). I joined an image to see better.
I also made this code to render overlays :
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKTileOverlay class]]) {
return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay];
}
else if ([overlay isKindOfClass:[MKPolyline class]]) {
MKPolylineRenderer *lineView = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
lineView.strokeColor = [UIColor greenColor];
lineView.lineWidth = 3;
return lineView;
}
return nil;
}
It's like the "tile" that render the line hide the tile loaded from the MKTileOverlay. How can I :
- specify that I the MKPolyline overlay must be transparent ?
- reload the background tile ?
Screeshot :
See the tile with line has no background anymore http://sigmanet.ch/tmp/screen.png
After days of work, here is my own solution.
Extend MKPolylineRenderer and add a reference to the MKTileOverlayRenderer. Let's call this new class MCPolylineRenderer.
In this class, override this two methods :
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
// Draw path only if tile render has a tile
if ([self.tileRenderRef canDrawMapRect:mapRect zoomScale:zoomScale]) {
[super drawMapRect:mapRect zoomScale:zoomScale inContext:context];
}
}
- (BOOL)canDrawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale {
// We can draw path only if tile render can also
return [self.tileRenderRef canDrawMapRect:mapRect zoomScale:zoomScale];
}
Now, in the mapView:renderedForOverlay method, replace
MKPolylineRenderer *lineView = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
with
MCPolylineRenderer *lineView = [[MCPolylineRenderer alloc] initWithPolyline:overlay];
lineView.tileRenderRef = self.tileRender;
Also, you need to be sure that the loadTileAtPath:result: method doesn't result a tile when there is nothing to render (like a "tile not found" image).
This code will have effect that when there is no background tile to render, the path won't be draw neither.
You'll have to subclass MKPolylineRenderer to synchronize renderers drawing abilities.
import Foundation
import MapKit
class MyPolylineRenderer: MKPolylineRenderer {
var tileRenderer: MKTileOverlayRenderer?
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
if (tileRenderer?.canDraw(mapRect, zoomScale: zoomScale) ?? true) {
super.draw(mapRect, zoomScale: zoomScale, in: context)
}
}
override func canDraw(_ mapRect: MKMapRect, zoomScale: MKZoomScale) -> Bool {
return tileRenderer?.canDraw(mapRect, zoomScale: zoomScale) ?? super.canDraw(mapRect, zoomScale: zoomScale)
}
}
Then in your MKMapViewDelegate, keep a reference to your tileRenderer and implement the rendererForOverlay :
var tileRenderer: MKTileOverlayRenderer?
public func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let polyline = overlay as? MKPolyline {
let lineRenderer = NXPolylineRenderer(overlay: overlay)
lineRenderer.tileRenderer = tileRenderer
// Configure your polyline overlay renderer here...
return lineRenderer;
}
if let tileOverlay = overlay as? MKTileOverlay {
if tileRenderer == nil || tileRenderer.overlay != overlay {
tileRenderer = MKTileOverlayRenderer(overlay: overlay)
}
return tileRenderer
}
return MKOverlayRenderer(overlay: overlay)
}
All credits for the idea goes to #Jonathan, I'm just posting a swift ready to copy/paste code for newcomers.

Best way to change the background color for an NSView

I'm looking for the best way to change the backgroundColor of an NSView. I'd also like to be able to set the appropriate alpha mask for the NSView. Something like:
myView.backgroundColor = [NSColor colorWithCalibratedRed:0.227f
green:0.251f
blue:0.337
alpha:0.8];
I notice that NSWindow has this method, and I'm not a big fan of the NSColorWheel, or NSImage background options, but if they are the best, willing to use.
Yeah, your own answer was right. You could also use Cocoa methods:
- (void)drawRect:(NSRect)dirtyRect {
// set any NSColor for filling, say white:
[[NSColor whiteColor] setFill];
NSRectFill(dirtyRect);
[super drawRect:dirtyRect];
}
In Swift:
class MyView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// #1d161d
NSColor(red: 0x1d/255, green: 0x16/255, blue: 0x1d/255, alpha: 1).setFill()
dirtyRect.fill()
}
}
An easy, efficient solution is to configure the view to use a Core Animation layer as its backing store. Then you can use -[CALayer setBackgroundColor:] to set the background color of the layer.
- (void)awakeFromNib {
self.wantsLayer = YES; // NSView will create a CALayer automatically
}
- (BOOL)wantsUpdateLayer {
return YES; // Tells NSView to call `updateLayer` instead of `drawRect:`
}
- (void)updateLayer {
self.layer.backgroundColor = [NSColor colorWithCalibratedRed:0.227f
green:0.251f
blue:0.337
alpha:0.8].CGColor;
}
That’s it!
If you are a storyboard lover, here is a way that you don't need any line of code.
Add NSBox as a subview to NSView and adjust NSBox's frame as the same with NSView.
In Storyboard or XIB change Title position to None, Box type to Custom, Border Type to "None", and Border color to whatever you like.
Here is a screenshot:
This is the result:
If you setWantsLayer to YES first, you can directly manipulate the layer background.
[self.view setWantsLayer:YES];
[self.view.layer setBackgroundColor:[[NSColor whiteColor] CGColor]];
Think I figured out how to do it:
- (void)drawRect:(NSRect)dirtyRect {
// Fill in background Color
CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetRGBFillColor(context, 0.227,0.251,0.337,0.8);
CGContextFillRect(context, NSRectToCGRect(dirtyRect));
}
edit/update: Xcode 8.3.1 • Swift 3.1
extension NSView {
var backgroundColor: NSColor? {
get {
guard let color = layer?.backgroundColor else { return nil }
return NSColor(cgColor: color)
}
set {
wantsLayer = true
layer?.backgroundColor = newValue?.cgColor
}
}
}
usage:
let myView = NSView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
print(myView.backgroundColor ?? "none") // NSView's background hasn't been set yet = nil
myView.backgroundColor = .red // set NSView's background color to red color
print(myView.backgroundColor ?? "none")
view.addSubview(myView)
I went through all of these answers and none of them worked for me unfortunately. However, I found this extremely simple way, after about an hour of searching : )
myView.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 0.9);
Best Solution :
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.wantsLayer = YES;
}
return self;
}
- (void)awakeFromNib
{
float r = (rand() % 255) / 255.0f;
float g = (rand() % 255) / 255.0f;
float b = (rand() % 255) / 255.0f;
if(self.layer)
{
CGColorRef color = CGColorCreateGenericRGB(r, g, b, 1.0f);
self.layer.backgroundColor = color;
CGColorRelease(color);
}
}
In Swift:
override func drawRect(dirtyRect: NSRect) {
NSColor.greenColor().setFill()
NSRectFill(dirtyRect)
super.drawRect(dirtyRect)
}
Use NSBox, which is a subclass of NSView, allowing us to easily style
Swift 3
let box = NSBox()
box.boxType = .custom
box.fillColor = NSColor.red
box.cornerRadius = 5
Without doubt the easiest way, also compatible with Color Set Assets:
Swift:
view.setValue(NSColor.white, forKey: "backgroundColor")
Objective-C:
[view setValue: NSColor.whiteColor forKey: "backgroundColor"];
Interface Builder:
Add a user defined attribute backgroundColor in the interface builder, of type NSColor.
Just set backgroundColor on the layer (after making the view layer backed).
view.wantsLayer = true
view.layer?.backgroundColor = CGColor.white
I tested the following and it worked for me (in Swift):
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blackColor().colorWithAlphaComponent(0.5).CGColor
In Swift 3, you can create an extension to do it:
extension NSView {
func setBackgroundColor(_ color: NSColor) {
wantsLayer = true
layer?.backgroundColor = color.cgColor
}
}
// how to use
btn.setBackgroundColor(NSColor.gray)
In swift you can subclass NSView and do this
class MyView:NSView {
required init?(coder: NSCoder) {
super.init(coder: coder);
self.wantsLayer = true;
self.layer?.backgroundColor = NSColor.redColor().CGColor;
}
}
This supports changing systemwide appearance (turning dark mode on or off) while the application is running. You can also set the background colour in Interface Builder, if you set the class of the view to BackgroundColorView first.
class BackgroundColorView: NSView {
#IBInspectable var backgroundColor: NSColor? {
didSet { needsDisplay = true }
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
wantsLayer = true
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
wantsLayer = true
}
override var wantsUpdateLayer: Bool { return true }
override func updateLayer() {
layer?.backgroundColor = backgroundColor?.cgColor
}
}
Have a look at RMSkinnedView. You can set the NSView's background color from within Interface Builder.
Just small reusable class (Swift 4.1)
class View: NSView {
var backgroundColor: NSColor?
convenience init() {
self.init(frame: NSRect())
}
override func draw(_ dirtyRect: NSRect) {
if let backgroundColor = backgroundColor {
backgroundColor.setFill()
dirtyRect.fill()
} else {
super.draw(dirtyRect)
}
}
}
// Usage
let view = View()
view.backgroundColor = .white