Changing AVPlayerView background color - swift

Is it possible to change the background color of a AVPlayerView when used in a macOS application. I want to do this to remove the black bars when playing a video.
I've tried the following:
videoView.contentOverlayView?.wantsLayer = true
videoView.contentOverlayView?.layer?.backgroundColor = NSColor.blue.cgColor
also tried adding these:
view.wantsLayer = true
videoView.wantsLayer = true
but the background is still black.

AVPlayerView does not have layer after the initialization or setting wantsLayer property, but creates it later at some point. I was able to change the background with the next code in my AVPlayerView subclass:
override var layer: CALayer? {
get { super.layer }
set {
newValue?.backgroundColor = CGColor.clear
super.layer = newValue
}
}

videoView.contentOverlayView?.layer?.setNeedsDisplay()
try this maybe you just need to update the view. However if you would post more of your code I could try to help more.

Related

Add alpha to parentVC.view

I am trying to add alpha to the background view when tapped on a button. So far achieved adding blur but alpha not so much.
How can I add alpha to the background so that when the bottom sheet appears background will be darker and disabled.
let maxDimmedAlpha: CGFloat = 0.2
lazy var dimmedView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.alpha = maxDimmedAlpha
return view
}()
#objc func shareBtnClick() {
dimmedView.frame = self.parentVC.view.bounds
dimmedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.parentVC.view.addSubview(dimmedView)
if self.parentVC.navigationController != nil {
if self.parentVC.navigationController?.viewControllers.count == 1 {
showBottomSheet()
} else {
NotificationCenter.default.post(name: NSNotification.Name("ShowBottomSheet"), object: nil, userInfo: ["itemId": modalSheet(), "delegate": self])
}
} else {
showBottomSheet()
}
}
func showBottomSheet() {
let modalSheet = MainBottomSheet()
modalSheet.data = self.modalSheet()
modalSheet.delegate = self
modalSheet.modalPresentationStyle = .overCurrentContext
self.parentVC.present(modalSheet, animated: true)
}
I was able to produce the dimmed effect using this code in XCode, I'm not sure why it won't work in your project but there is an easy way to debug this.
I suggest using Debug View Hierarchy, one of XCode's best tools in my opinion. This allows you to separate every single layer of the user interface. This way, you can see if your dimmedView is actually being added to the parent view and that its frame is matching the parent view's bounds.
Keep in mind if your background is dark, you won't see this dimmedView because its backgroundColor is set to UIColor.black.
Debug View Hierarchy button

Statusbar.image has wrong color

I am using following code for my status bar image:
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
This leads to wrong color for certain background colors / modes. In the picture, what's white should be black. The image resource is white/transparent. If I change that, I get the same problem. All other status bar images will turn white on certain configurations, mine will stay black.
I was thinking that MacOS would add effects to make all statusbar icons look uniform on it's own, but apparently thats not the case...
Any ideas how to fix that?
Thanks!
MacOs can do what you want. I recommend reading Apple documentation:
https://developer.apple.com/documentation/uikit/appearance_customization/supporting_dark_mode_in_your_interface
Basically you have 2 options if you don‘t provide the code manually.
Option 1. In Xcode, navigate to your image asset in assets.xcassets. In the attributes pane, in „Render as…“ specify „Template Image“. This worked well for my menu bar app.
Option 2. Supply different versions of your icon in one image asset, macOs will then choose the appropriate version.
I found a solution. Again I realize that MacOS development is way less supported by Apple than iOS. I think the color adjustment of statusbar icons should be the task of the operating system, but Apple lets the developer do the work. Whatever.
Here is the solution:
You have to provide two versions of your icon, one in black, the other in white.
When the app launches, you have to check wether the user's MacOs is in dark or light mode. This can be done with following code:
let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
if (mode == "Dark"){
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
} else {
let icon = NSImage(imageLiteralResourceName:"flagDark")
statusBarItem.image = icon
}
One problem remains here now: When the user changes the mode while your app is running, the icon color won't update. Also: If the user uses the automatic mode (i.e. it's light at day and dark in the night), the icon color won't switch as well.
You can tackle that problem by listening to a certain notification that is fired when the dark mode settings changes:
DistributedNotificationCenter.default.addObserver(self, selector: #selector(updateIcon), name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil)
#objc func updateIcon(){
print("updateIcon ausgeführt")
let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
if (mode == "Dark"){
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
} else {
let icon = NSImage(imageLiteralResourceName:"flagDark")
statusBarItem.image = icon
}
}
In my tests, this worked in all scenarios.
For me this worked:
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
// ...
func applicationDidFinishLaunching(_ aNotification: Notification) {
// ...
if let button = statusItem.button {
let image = NSImage(named: NSImage.Name("TrayIcon"))
image?.isTemplate = true
button.image = image
}
// ...
}
// ...
}

How to create block screen with circle loader

I am doing an app that does background job that can take some time
I want to show a loader in that time
I want a black screen with a simple loader in the front of it
and show it \ hide it,
when I do actions in the background
I want to do a simple half black square with loader circle
that also blocks presses to the screen
Like in this picture:
How can I achieve that and that ?
First create one UIView which you will put in front of your LogIn view. Then add UIActivityIndicatorView to the created UIView.
let loadingIndicatorView = UIView()
let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
Now the loadingIndicatorView should have same frame size as your LogIN view. For color you can set your own color with alpha as you want to show LogIn content too. Initially keep it hidden and whenever you want to show it unhide it.
loadingIndicatorView.frame = view.frame
loadingIndicatorView.backgroundColor = .gray
loadingIndicatorView.isHidden = true
Now setup activityIndicatorView, it should be shown at centre,
activityIndicatorView.center = CGPoint(
x: UIScreen.main.bounds.size.width / 2,
y: UIScreen.main.bounds.size.height / 2
)
You can set some color to the indicator,
activityIndicatorView.color = .white
activityIndicatorView.hidesWhenStopped = true
Now add this activityIndicatorView to loadingIndicatorView and loadingIndicatorView to LogIn View.
loadingIndicatorView.addSubview(activityIndicatorView)
view.addSubview(loadingIndicatorView)
Lastly for showing do,
loadingIndicator.startAnimating()
loadingIndicatorView.isHidden = false
And for hiding,
loadingIndicator.stopAnimating()
loadingIndicatorView.isHidden = true
Updated Answer
Since the OP wanted an example code. Hence the updated answer. Hope everyone gets to learn something or the other out of it.
To start with, I created a subclass of UIView and named it PSOverlaySpinner and it looks something like below:
import UIKit
class PSOverlaySpinner: UIView {
//MARK: - Variables
private var isSpinning: Bool = false
private lazy var spinner : UIActivityIndicatorView = {
var spinner = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.white)
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.hidesWhenStopped = true
return spinner
}()
// MARK: - View Lifecycle Functions
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
super.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.init(white: 0.0, alpha: 0.8)
self.isSpinning = false
self.isHidden = true
createSubviews()
}
deinit {
self.removeFromSuperview()
}
func createSubviews() -> Void {
self.addSubview(spinner)
setupAutoLayout()
}
// MARK: - Private Methods
private func setupAutoLayout() {
if #available(iOS 11.0, *) {
spinner.safeAreaLayoutGuide.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor).isActive = true
spinner.safeAreaLayoutGuide.centerYAnchor.constraint(equalTo: safeAreaLayoutGuide.centerYAnchor).isActive = true
} else {
// Fallback on earlier versions
spinner.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
spinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
}
// MARK: - Public Methods
public func show() -> Void {
DispatchQueue.main.async {
if !self.spinner.isAnimating {
self.spinner.startAnimating()
}
self.isHidden = false
}
isSpinning = true
}
public func hide() -> Void {
DispatchQueue.main.async {
if self.spinner.isAnimating {
self.spinner.stopAnimating()
}
self.isHidden = true
}
isSpinning = false
}
}
Now move onto the ViewController that you want to add this overlay view to. Since I create my views programmatically, I will show how to do it the same way, but you can easily do it via storyboard or xibs.
Step 1 : Initialize
public lazy var spinnerView : PSOverlaySpinner = {
let loadingView : PSOverlaySpinner = PSOverlaySpinner()
return loadingView
}()
Step 2 : Add as a subview
self.view.addSubview(spinnerView)
Step 3 : Set constraints
spinnerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
spinnerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
spinnerView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
spinnerView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
Step 4 : To show PSOverlaySpinner
spinnerView.show()
Step 5 : To hide PSOverlaySpinner
spinnerView.hide()
That is it!!
If you want you can go ahead and modify the PSOverlaySpinner as per your needs. For example, you might want to add a UILabel below the spinner indicating him of the type of action taking place and so on.
Before
After
Old Answer
If you wish to do it manually then create a UIView with the its frame matching self.view.bounds, with 0.5-0.7 alpha and black background color. Add UIActivityIndicator as its subview constrained to its center. For a spinner specific to the image you will have to use the open sourced spinners made available. A couple of them can be found here. Once done add this view as the topmost subview in self.view.
You need to import this library SVProgressHUD and then set few properties like as follows:
SVProgressHUD.setDefaultStyle(SVProgressHUDStyle.dark)
SVProgressHUD.setBackgroundColor(.clear)
SVProgressHUD.setForegroundColor(.white)
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.show()
//SVProgressHUD.show(withStatus: "Loading something, Loading something,Loading something ...")
This will produce same UI output as needed by you in OP. You can find a running sample at my repository (TestPreLoader)

Sliding Animation NSView background color in swift

Trying to modifying the color of NSView with sliding animation like Google Trends
let hexColors = ["56A55B", "4F86EC", "F2BC42", "DA5040"]
#IBAction func changeColor(sender: NSButton) {
let randomIndex = Int(arc4random_uniform(UInt32(hexColors.count)))
NSAnimationContext.runAnimationGroup({_ in
//duration
NSAnimationContext.current.duration = 5.0
self.view.animator().layer?.backgroundColor = NSColor(hex: hexColors[randomIndex]).cgColor
}, completionHandler:{
print("completed")
})
}
I tried using NSAnimationContext to set duration of color change, but it does not work. However it works with the alphaValue of the view.
I'm not sure if you have gotten your answer yet. But this might be able get it to work:
let hexColors = ["56A55B", "4F86EC", "F2BC42", "DA5040"]
#IBAction func changeColor(sender: NSButton) {
let randomIndex = Int(arc4random_uniform(UInt32(hexColors.count)))
NSAnimationContext.runAnimationGroup({ context in
//duration
context.duration = 5.0
// This is the key property that needs to be set
context.allowsImplicitAnimation = true
self.view.animator().layer?.backgroundColor = NSColor(hex: hexColors[randomIndex]).cgColor
}, completionHandler:{
print("completed")
})
}
here is what the documentation says:
/* Determine if animations are enabled or not. Using the -animator proxy will automatically set allowsImplicitAnimation to YES. When YES, other properties can implicitly animate along with the initially changed property. For instance, calling [[view animator] setFrame:frame] will allow subviews to also animate their frame positions. This is only applicable when layer backed on Mac OS 10.8 and later. The default value is NO.
*/
#available(macOS 10.8, *)
open var allowsImplicitAnimation: Bool

NSWindow transition animation for View Controller Segues

I've been trying to figure out how to get a NSWindow to perform a transition animation with Swift 3. I found a few examples in Objective-C, but I haven't been able to tease out the relevant details and translate into the target language / newer SDK and get it applied to the right object. This one is pretty flipping cool, but it's ~8yrs old: http://www.theregister.co.uk/2009/08/21/cocoa_graphics_framework/ -- I would imagine there's a better way to do the CGSCube effect now in macOS Sierra with Swift.
Here's what I have so far:
class ViewController: NSViewController {
func doAnimation() {
if let layer = view .layer {
layer.backgroundColor = CGColor.black
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(CGFloat.pi * 2.0)
rotateAnimation.duration = 10.0
layer.add(rotateAnimation, forKey: nil)
}
}
override func viewWillAppear() {
if let window = self.view.window {
window.styleMask = NSWindowStyleMask.borderless
window.backingType = NSBackingStoreType.buffered
window.backgroundColor = NSColor.clear
window.isOpaque = false
window.hasShadow = false
}
}
override func viewDidLoad() {
super.viewDidLoad()
doAnimation()
}
}
This doesn't really do the trick at all. I get a white background on my window instead of a transparent background, and my view rolls across the frame instead of the window frame itself being animated.
Ideally, I would like to do something more advanced like these 3d transitions -- https://cocoapods.org/pods/MKCubeController & https://www.appcoda.com/custom-view-controller-transitions-tutorial/ & https://github.com/andresbrun/ABCustomUINavigationController#cube but I'm not quite sure how to translate the examples from the iOS SDK over to the macOS SDK without UIKit. (Annoyingly, I remember pulling this off a few years back in ObjC, but the project was lost somewhere between formats / new computers.)
How can I apply a transform to the NSWindow itself while segueing between View Controllers? Any tips toward adding some 3d to this effect would be appreciated. I hoping there's maybe a CocoaPod that gets me halfway there.