How to make native (realtime) blur in Sprite-Kit? - objective-c

I am using this code for my blur, but it doesn't seem to work. Or it kinda works. I can only see a grey coloured background instead of the blurred scene. (First picture, I have captured it using a screenshot, but that captures it like its working great, weird.) And when i swipe down, it blurs the scene normally.
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
[blurEffectView setFrame:self.view.bounds];
[self.view addSubview:blurEffectView];
The screenshot captures it normally but i can only see grey background.
Here when i swipe down, it works like charm.
I have found different solutions for this, but those were only static. I want this type of blur, it doesn't even drop any fps. Is there a way to get this working?

Since
The blur effect is provided by a Core Image Filter.
Every Core Image Filter can be used with a SKEffectNode
SKScene is an SKEffectNode
then...
class GameScene: SKScene {
override func didMove(to view: SKView) {
super.didMove(to:view)
self.filter = CIFilter(name: "CIGaussianBlur", withInputParameters: ["inputRadius": 10])
self.shouldEnableEffects = true
}
}

Related

Custom NSView background color not changing when switching in and out of dark mode

I cannot figure out how to update the background color of my custom NSView when the user switches in and out of dark mode.
I've read the documentation and followed instructions here:detecting darkmode
The strange thing is that I can get all the subviews to behave correctly, but for some strange reason I can't get the background color of the main view to change. The background color of the view looks correct when I start the app in either mode, but when I switch between modes while the app is running it doesn't update to the new theme.
Would be grateful for any suggestions.
Inside the Custom NSView I have the method
- (void) viewDidChangeEffectiveAppearance
{
self.needsDisplay = YES;
}
and inside the drawRect I have a do a simple color change before continuing with drawing in the view
NSAppearance *currentAppearance = [NSAppearance currentAppearance];
if (#available(*, macOS 10.14)) {
if(currentAppearance.name == NSAppearanceNameDarkAqua) {
red = 0.5*red+0.5;
green = 0.5*green+0.5;
blue = 0.5*blue+0.5;
}
}
Here is a screenshot of darkmode before (the way it should look)
Dark Mode Before
Here is a screenshot of light mode after user switch
Light Mode After
Here is a screenshot of lightmode before (the way it should look)
Light Mode Before
And here is a screenshot of darkmode after user switch
Dark Mode After
ps The reason I'm baffled and have little code to post is that the correct behavior is supposed to happen automatically with little effort. I even deleted the view from the nib and rebuilt it thinking maybe some setting got corrupted, but that didn't solve the problem.
Update: I found the source of the problem. This method gets called in windowDidLoad
- (void) setTransparent:(BOOL)transparent
{
if(transparent) {
[self.window setOpaque:NO];
NSColor *backgroundColor = [NSColor windowBackgroundColor];
backgroundColor = [backgroundColor colorWithAlphaComponent: .75];
[self.window setBackgroundColor:backgroundColor];
self.window.alphaValue = 0.75;
}
else {
[self.window setOpaque:YES];
NSColor *backgroundColor = [NSColor windowBackgroundColor];
backgroundColor = [backgroundColor colorWithAlphaComponent: 1];
[self.window setBackgroundColor:backgroundColor];
self.window.alphaValue = 1;
}
}
I get the expected behavior if I comment out the call to this method.
Why did this cause me to lose the automatic behavior of background color change when the user changes between light and dark mode?
My guess is that you’re not actually using the standard color for the background color.
You are using [NSColor windowBackgroundColor], however then making a copy with a different alpha component (via colorWithAlphaComponent), making it no longer a standard color.
My guess is a lot of the automatic ‘just works’ behaviour happens when you use the standard color definitions. As a test, could you try removing the colorWithAlphaComponent calls (where you are adding the transparency) from your settransparent method and see whether it works? if it does, you might need to find another way to add transparency to your view if you want the automatic behaviour.

XCode 8: Puzzling side effect of setting label.text

It appears that setting the text of a label can have the side effect of increasing the opacity of a MKCircle that has been added to an MKMapView.
Here is what's going on:
First I have this delegate function to handle rendering my overylay
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let diskRenderer: MKCircleRenderer = MKCircleRenderer.init(overlay: overlay)
diskRenderer.fillColor = UIColor.init(red: 0, green: 255, blue: 255, alpha: 0.10)
diskRenderer.strokeColor = UIColor.init(red:0, green: 0, blue: 0, alpha: 0.25)
return diskRenderer
}
Then I add it to the MKMapView
map.delegate = self
let diskOverlay: MKCircle = MKCircle.init(center: location, radius: diskRadius)
map.add(diskOverlay)
All seems good. Now at the same time, I have a custom UIControl called YSRangeSlider that I got from cocoapods. When the user moves the thumbs on this slider, a function gets called that updates the text of some labels according to whatever the current values are on the slider. Here is that function.
func setPlayerLabels() {
let lowerValue = Int(playersSlider.minimumSelectedValue)
let upperValue = Int(playersSlider.maximumSelectedValue)
minPlayersLabel.text = "\(lowerValue)"
maxPlayersLabel.text = "\(upperValue)"
}
Now here is the really really weird thing. When I launch the app and segue to this ViewController and then start sliding a thumb on the slider, the MKCircle - that is the faint disk being shown on the map, becomes more and more opaque until it is solid filled. But if I comment out the two label.text = "---" going on in the function above, this strange behavior no longer occurs.
Below you can see the overlay as it looks initially (low opacity) and then after getting messed up (opaque). I don't know the deeper reason of why this is happening nor how to fix it. This is XCode 8 with Swift 3.
To make things even worse, I should mention that sometimes the map renders the disk overlay as if it was on a large square tile casting a shadow beneath it, a shadow who's opacity goes towards opaque just like the overlay itself. Also if I zoom out some before touching the slider, it so happens that sliding a thumb on the slider then causes the map to zoom back in on top of the already mentioned opacity changes.
For anyone interested, I figured out how to solve this problem, however the fundamental cause and effect behind it is still a bit lost on me.
What's happening is whenever those label.text = "---" assignments get called, the viewDidLayoutSubviews() function also gets called. Since the MKMapView's overlay and region get set inside viewDidLayoutSubviews, it so happens that the overlay gets redrawn on top of the existing one, making it appear to get darker and darker, and hence the map zooms back into the region if I had already zoomed out.
What I still don't understand is why executing label.text = "---" causes the viewDidLayoutSubviews function to get called.

How to make blur effect when view controller slides out

I've got a program with slide out menu. There's Menu button(BarButtonItem) in the right position. When the view controller loads I'm doing next
_menuBarButton.target = self.revealViewController;
_menuBarButton.action = #selector(revealToggle:);
So, when I click on the this button the View Controller slides out to right and I see another View Controller.
So, I want to make the main view controller blur when it slides out. I've got a code how to do it blur, but I can't implement this code because when I tap on the bat button it runs revealToggle: selector.
I've tried:
1. To set action for bar button. And firstly blur view controller:
- (IBAction)menuBarButtonTapped:(UIBarButtonItem *)sender {
[self setBlurEffect];
_menuBarButton.target = self.revealViewController;
_menuBarButton.action = #selector(revealToggle:);
[self.menuBarButton.target performSelector:#selector(revealToggle:)];
}
But app crashes with "unrecognized selector" (the solutions of this problem doesn
t help too).
I've wanted to use willDisapear method but it doesn't run, because the main view controller doesn't disapear. It just slides out.
So, could you help me with this problem?
P.S. I'll be happy if you propose any other effects for main view controller except blur.
P.P.S. Sorry for many mistakes in question.
I've found a good answer for me. I don't use several ViewControllers to implement menu panel.
Now I'm using another UIView calls menuPanel. It contains tableView(UITableView) and blurView(UIView). The second one is under first one.
You can blur this view in to ways:
From the code using next method.
(void) setBlurEffect {// Add blur view
CGRect boundsView = self.someView.bounds;
UIVisualEffectView *tableViewVisualEffect = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]];
self.tableViewVisualEffect.frame = boundsView;
self.tableViewVisualEffect.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.tableViewVisualEffect.backgroundColor = [UIColor clearColor];
[UIView transitionWithView:self.view
duration:0.3
options:UIViewAnimationOptionTransitionCrossDissolve
animations: ^ {
[self.someView addSubview:self.tableViewVisualEffect];
}
completion: nil ];
// Here you can add visual effects to any UIView control.
// Replace custom view with navigation bar in above code to add effects to custom view.
}
Where someView is view you want to do blur(or any other effect)
From the story board with Visual Effect View.(I've chosen)
After all I've set tableView and blurView into menuPanelView as pinned to all sides of the menuPanel with 0 distance.
And the last thing I've done I'm changing the position of the menuPanelView with animation:
[self.menuPanelView setFrame:self.visibleTableViewFramePosition];
[UIView animateWithDuration:0.8
animations:^{
[self.menuPanelView setFrame:self.invisibleTableViewFramePosition];
} completion:nil];
Where invisibleTableViewFramePosition is CGRect variable contains position of the menuPanelView when it has to be invisible. You also need to create visibleTableViewFramePosition.
That's all. Hope it may be helpful for someone=)

NSStatusItem change image for dark tint

With OSX 10.10 beta 3, Apple released their dark tint option. Unfortunately, it also means that pretty much all status bar icons (with the exception of Apple's and Path Finder's that I've seen), including mine, remain dark on a dark background. How can I provide an alternate image for when dark tint is applied?
I don't see an API change on NSStatusBar or NSStatusItem that shows me a change, I'm assuming it's a notification or something reactive to easily make the change as the user alters the tint.
Current code to draw the image is encased within an NSView:
- (void)drawRect:(NSRect)dirtyRect
{
// set view background color
if (self.isActive) {
[[NSColor selectedMenuItemColor] setFill];
} else {
[[NSColor clearColor] setFill];
}
NSRectFill(dirtyRect);
// set image
NSImage *image = (self.isActive ? self.alternateImage : self.image);
_imageView.image = image;
}
TL;DR: You don't have to do anything special in Dark Theme. Give NSStatusItem (or NSStatusBarButton) a template image and it will style it correctly in any menubar context.
The reason why some apps' status items (such as PathFinder's) already work in Dark Theme is because they're not setting their own custom view on the StatusItem, but only setting a template image on the StatusItem.
Something like:
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
NSImage *image = [NSImage imageNamed:#"statusItemIcon"];
[image setTemplate:YES];
[_statusItem setImage:image];
This works exactly as you'd expect in Mavericks and earlier, as well as Yosemite and any future releases because it allows AppKit to do all of the styling of the image depending on the status item state.
Mavericks
In Mavericks (and earlier) there were only 2 unique styles of the items. Unpressed and Pressed. These two styles pretty much looked purely black and purely white, respectively. (Actually "purely black" isn't entirely correct -- there was a small effect that made them look slightly inset).
Because there were only two possible state, status bar apps could set their own view and easily get the same appearance by just drawing black or white depending on their highlighted state. (But again note that it wasn't purely black, so apps either had to build the effect in the image or be satisfied with a hardly-noticeable out of place icon).
Yosemite
In Yosemite there are at least 32 unique styling of items. Unpressed in Dark Theme is only one of those. There is no practical (or unpractical) way for an app to be able to do their own styling of items and have it look correct in all contexts.
Here are examples of six of those possible stylings:
Status items on an inactive menubar now have a specific styling, as opposed to a simple opacity change as in the past. Disabled appearance is one other possible variation; there are also other additional dimensions to this matrix of possibilities.
API
Arbitrary views set as NSStatusItem's view property have no way to capture all of these variations, hence it (and other related API) is deprecated in 10.10.
However, seed 3 introduces new API on NSStatusItem:
#property (readonly, strong) NSStatusBarButton *button NS_AVAILABLE_MAC(10_10);
This piece of API has a few purposes:
An app can now get the screen position (or show a popover from) a status item without setting its own custom view.
Removes the need for API like image, title, sendActionOn: on NSStatusItem.
Provides a class for new API: i.e. looksDisabled. This allows apps to get the standard disabled/off styling (like Bluetooth/Time Machine when off) without requiring a custom image.
If there's something that can't be done with the current (non- custom view) API, please file an enhancement request for it. StatusItems should provide behavior or appearances in a way that it standard across all status items.
More discussion is at https://devforums.apple.com/thread/234839, although I've summarized most everything here.
I end up did something like following to my custom drag and drop NSStatusItemView: (Using Swift)
var isDark = false
func isDarkMode() {
isDark = NSAppearance.currentAppearance().name.hasPrefix("NSAppearanceNameVibrantDark")
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
isDarkMode()
// Now use "isDark" to determine the drawing colour.
if isDark {
// ...
} else {
// ...
}
}
When the user changed the Theme in System Preferences, the NSView will be called by the system for re-drawing, you can change the icon colour accordingly.
If you wish to adjust other custom UI outside this view, you can either use KVO to observer the isDark key of the view or do it on your own.
I created a basic wrapper around NSStatusItem that you can use to provide support for 10.10 and earlier with custom views in the status bar. You can find it here: https://github.com/noahsmartin/YosemiteMenuBar The basic idea is to draw the custom view into a NSImage and use this image as a template image for the status bar item. This wrapper also forwards click events to the custom view so they can be handled the same way as pre 10.10. The project contains a basic example of how YosemiteMenuBar can be used with a custom view on the status bar.
Newest swift code set image template method is here:
// Insert code here to initialize your application
if let button = statusItem.button {
button.image = NSImage(named: "StatusIcon")
button.image?.isTemplate = true // Just add this line
button.action = #selector(togglePopover(_:))
}
Then it will change the image when dark mode.
When your application has drawn any GUI element you can get its appearance via [NSAppearance currentAppearance] which itself has a name property that holds something like
NSAppearanceNameVibrantDark->NSAppearanceNameAqua->NSAppearanceNameAquaMavericks
The first part is the appearance’s name, which is also available as a constant in NSAppearanceNameVibrantDark or NSAppearanceNameVibrantLight.
I don’t know if there’s a way to get just the first part, but I think this does the trick for now.
Example code:
-(void)awakeFromNib {
NSStatusItem* myStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
myStatusItem.title = #"Hello World";
if ([[[NSAppearance currentAppearance] name] containsString:NSAppearanceNameVibrantDark]) {
myStatusItem.title = #"Dark Interface";
} else {
myStatusItem.title = #"Light Interface";
}
}
But just in case you do want to monitor the status changes you can. I also know there is a better way to determine lite/dark mode than what's been said above, but I can remember it right now.
// Monitor menu/dock theme changes...
[[NSDistributedNotificationCenter defaultCenter] addObserver: self selector: #selector(themeChange:) name:#"AppleInterfaceThemeChangedNotification" object: NULL];
//
-(void) themeChange :(NSNotification *) notification
{
NSLog (#"%#", notification);
}

How to fade picture loading process with an UIActivityIndicatorView?

I have created a custom UITableViewCell. My custom cell contents an ImageView for picture of current article. On creation of the cell image will be loaded from Internet and displayed in the UIImageView. How can I fade then downloading process with an ActivityIndicatorView?
I'm sorry for this bad English :-) I use an online translator.
If I'm understanding you correctly, it seems like you want your UIImageView's (that are lazily downloaded in realtime) to fade in once they have fully downloaded. And while they are downloading, display the spinning UIActivityIndicatorView wheel.
Here is what I suggest:
1) Instead of defining the custom view in your table cell as a UIImageView specifically, just use the more generic UIView. This is because both classes (UIImageView and UIActivityIndicatorView) are subclasses and can be set as such.
2) Initially, for any and all cells, set the UIView to the UIActivityIndicatorView (don't forget to use "startAnimating" to get it to spin) and then on the callback function for the download completion, go to the appropriate cell and set that custom UIView to the downloaded UIImageView.
3) To achieve the fade in effect, look at the following code:
// Sets the image completely transparent
imageView.alpha = 0.0f;
// Animates the image to fade in fully over 1.0 second
[UIView animationWithDuration:1.0f animations:^{
imageView.alpha = 1.0f;
}];
4) You might need to call "setNeedsDisplay" on the table cell to refresh it's subviews after setting the new image view, and before animating it.