func draw(_ dirtyRect: NSRect) is not called - draw

I wrote this code in Swift 3.0 but draw method is not called from func drawBorder by needsDisplay = true; drawBorder is called by clicking button in another view.
Thanks for any hint.
class clsDrawView: NSView {
private var redraw = false
var border = NSBezierPath()
var color = NSColor()
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
if redraw {
color.setStroke()
border.stroke()
}
}
func drawBorder() {
redraw = true
color = NSColor.blue
border.lineWidth = CGFloat(10)
border.move(to: NSPoint(x: 20, y: 20))
border.line(to: NSPoint(x: 50, y: 50))
needsDisplay = true
}
}

Try to call setNeedsDisplay(_:) method on your view, this should call draw(_:) method. Never call draw(_:) method by yourself.

Finally I solved it via notification
class clsDrawView: NSView {
private var draw = false
private var border = NSBezierPath()
var color = NSColor()
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
NotificationCenter.default.addObserver(self, selector: #selector(self.drawBorder(_:)), name: NSNotification.Name(rawValue: "drawBorder"), object: nil)
// Drawing code here.
if draw {
color.setStroke()
border.stroke()
}
}
func drawBorder(_ notification: NSNotification) {
draw = true
color = NSColor.black
NSBezierPath.setDefaultLineWidth(4)
border.lineWidth = CGFloat(10)
border = NSBezierPath(rect: self.bounds)
needsDisplay = true
}
}
Notification is postec from ViewController like this
#IBAction func btnDraw(_ sender: NSButton) {
NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "drawBorder"), object: sender)
}

Related

How to call function indirectly in Kotlin

Assume I have a mutableMap:
val MM = mutableMapOf()
Now I define a function as a method for it:
MM["testF"] = fun () {
println("WOW")
}
Now I want to call it in another place:
val MMTF = MM["testF"] as Function<*>
MMTF() <-- NOT WORKING
Any help will be appreciated.
This code will print bar
fun main() {
val map = mutableMapOf<String, () -> Any>()
map["foo"] = {
println("bar")
}
run(map["foo"]!!)
}

How to display view within a sheet programmatically in SwiftUI?

I am currently using Google Maps API in SwiftUI, and am trying to get a sheet to appear after a marker infoWindow is tapped programmatically.
In other parts of my app I’m displaying sheets like this, and it’s the same thing I’m trying to achieve here but programmatically:
https://blog.kaltoun.cz/swiftui-presenting-modal-sheet/
Right now I have a function that prints a message when an infoWindow is tapped, but don’t know how to make a SwiftUI view appear within a sheet using the function.
-
Since I’m using SwiftUI, the way I implement the Google Maps API is a little different than in plain Swift.
Here are the basics of my GMView.swift file that handles all the google maps stuff.
import SwiftUI
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
import Foundation
struct GoogMapView: View {
var body: some View {
GoogMapControllerRepresentable()
}
}
class GoogMapController: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate {
var locationManager = CLLocationManager()
var mapView: GMSMapView!
let defaultLocation = CLLocation(latitude: 42.361145, longitude: -71.057083)
var zoomLevel: Float = 15.0
let marker : GMSMarker = GMSMarker()
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.distanceFilter = 50
locationManager.startUpdatingLocation()
locationManager.delegate = self
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude, longitude: defaultLocation.coordinate.longitude, zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.isMyLocationEnabled = true
mapView.setMinZoom(14, maxZoom: 20)
mapView.settings.compassButton = true
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.settings.scrollGestures = true
mapView.settings.zoomGestures = true
mapView.settings.rotateGestures = true
mapView.settings.tiltGestures = true
mapView.isIndoorEnabled = false
marker.position = CLLocationCoordinate2D(latitude: 42.361145, longitude: -71.057083)
marker.title = "Boston"
marker.snippet = "USA"
marker.map = mapView
// Add the map to the view, hide it until we've got a location update.
view.addSubview(mapView)
// mapView.isHidden = true
}
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude, longitude: location.coordinate.longitude, zoom: zoomLevel)
if mapView.isHidden {
mapView.isHidden = false
mapView.camera = camera
} else {
mapView.animate(to: camera)
}
}
// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
// Display the map using the default location.
mapView.isHidden = false
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
}
struct GoogMapControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<GMControllerRepresentable>) -> GMController {
return GMController()
}
func updateUIViewController(_ uiViewController: GMController, context: UIViewControllerRepresentableContext<GMControllerRepresentable>) {
}
}
Here is the function I'm putting within GMView.swift's UIViewController (GMController) that I’m trying to use to make a view appear within a sheet:
// Function to handle when a marker's infowindow is tapped
func mapView(_ mapView: GMSMapView, didTapInfoWindowOf didTapInfoWindowOfMarker: GMSMarker) {
print("You tapped a marker's infowindow!")
return
}
Here is the view I’m trying to get to appear:
struct SortBy: View {
var body: some View {
VStack(alignment: .leading) {
Text("Sort By")
.font(.title)
.fontWeight(.black)
.padding(.trailing, 6)
Rectangle()
.fill(Color.blue)
.frame(width: 200, height: 200)
}
}
}
Does anyone know how I can get my function above to make a SwiftUI view appear within a sheet?
Use .sheet similarly like here:
https://blog.appsbymw.com/posts/how-to-present-and-dismiss-a-modal-in-swiftui-155c/
You will need:
In your UIViewControllerRepresentable, add a State object, let's say #State var showModal = false
In your parent view, Use .sheet(isPresented: $showModal) { CONTENT_VIEW(showModal: $showModal) }
In your UIViewControllerRepresentable, also add a Binding for showModal .
In your UIViewControllerRepresentable, use Coordinator to set your UIViewController and GMSMapViewDelegate
In your UIViewController, Now you can access the Binding via owner.showModal
Hope it helps. Good luck.

How to create notification badge in tableview

How to make a notification badge on the table view?
If there is new data it will display the badge on the table view, and if there is new data again, the badge will automatically be added.
I can strongly recommend you use the following Framework: swift-badge
use_frameworks!
target 'Your target name'
pod 'BadgeSwift', '~> 4.0'
Easy to use:
let badge = BadgeSwift()
view.addSubview(badge)
// Position the badge ...
Customization:
// Text
badge.text = "2"
// Insets
badge.insets = CGSize(width: 12, height: 12)
// Font
badge.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
// Text color
badge.textColor = UIColor.yellow
// Badge color
badge.badgeColor = UIColor.black
// Shadow
badge.shadowOpacityBadge = 0.5
badge.shadowOffsetBadge = CGSize(width: 0, height: 0)
badge.shadowRadiusBadge = 1.0
badge.shadowColorBadge = UIColor.black
// No shadow
badge.shadowOpacityBadge = 0
// Border width and color
badge.borderWidth = 5.0
badge.borderColor = UIColor.magenta
// Customize the badge corner radius.
// -1 if unspecified. When unspecified, the corner is fully rounded. Default: -1.
badge.cornerRadius = 10
Also. If you don't want to use pods, here is the full and ready to use class It also includes functions I have created:
import UIKit
/**
Badge view control for iOS and tvOS.
Project home: https://github.com/marketplacer/swift-badge
*/
#IBDesignable public class BadgeSwift: UILabel {
/// Background color of the badge
#IBInspectable public var badgeColor = Colors.red {
didSet {
setNeedsDisplay()
}
}
/// Width of the badge border
#IBInspectable public var borderWidth: CGFloat = 0 {
didSet {
invalidateIntrinsicContentSize()
}
}
/// Color of the bardge border
#IBInspectable public var borderColor = Colors.white {
didSet {
invalidateIntrinsicContentSize()
}
}
/// Badge insets that describe the margin between text and the edge of the badge.
#IBInspectable public var insets: CGSize = CGSize(width: 5, height: 2) {
didSet {
invalidateIntrinsicContentSize()
}
}
// MARK: Badge shadow
// --------------------------
/// Opacity of the badge shadow
#IBInspectable public var shadowOpacityBadge: CGFloat = 0.5 {
didSet {
layer.shadowOpacity = Float(shadowOpacityBadge)
setNeedsDisplay()
}
}
/// Size of the badge shadow
#IBInspectable public var shadowRadiusBadge: CGFloat = 0.5 {
didSet {
layer.shadowRadius = shadowRadiusBadge
setNeedsDisplay()
}
}
/// Color of the badge shadow
#IBInspectable public var shadowColorBadge = Colors.black {
didSet {
layer.shadowColor = shadowColorBadge.cgColor
setNeedsDisplay()
}
}
/// Offset of the badge shadow
#IBInspectable public var shadowOffsetBadge: CGSize = CGSize(width: 0, height: 0) {
didSet {
layer.shadowOffset = shadowOffsetBadge
setNeedsDisplay()
}
}
/// Initialize the badge view
convenience public init() {
self.init(frame: CGRect())
}
/// Initialize the badge view
override public init(frame: CGRect) {
super.init(frame: frame)
setup()
}
/// Initialize the badge view
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
/// Add custom insets around the text
override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
var insetsWithBorder = actualInsetsWithBorder()
let rectWithDefaultInsets = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)
// If width is less than height
// Adjust the width insets to make it look round
if rectWithDefaultInsets.width < rectWithDefaultInsets.height {
insetsWithBorder.width = (rectWithDefaultInsets.height - rect.width) / 2
}
let result = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)
return result
}
/// Draws the label with insets
override public func drawText(in rect: CGRect) {
layer.cornerRadius = rect.height / 2
let insetsWithBorder = actualInsetsWithBorder()
let insets = UIEdgeInsets(
top: insetsWithBorder.height,
left: insetsWithBorder.width,
bottom: insetsWithBorder.height,
right: insetsWithBorder.width)
let rectWithoutInsets = UIEdgeInsetsInsetRect(rect, insets)
super.drawText(in: rectWithoutInsets)
}
/// Draw the background of the badge
override public func draw(_ rect: CGRect) {
let rectInset = rect.insetBy(dx: borderWidth/2, dy: borderWidth/2)
let path = UIBezierPath(roundedRect: rectInset, cornerRadius: rect.height/2)
badgeColor.setFill()
path.fill()
if borderWidth > 0 {
borderColor.setStroke()
path.lineWidth = borderWidth
path.stroke()
}
super.draw(rect)
}
private func setup() {
textAlignment = NSTextAlignment.center
clipsToBounds = false // Allows shadow to spread beyond the bounds of the badge
}
/// Size of the insets plus the border
private func actualInsetsWithBorder() -> CGSize {
return CGSize(
width: insets.width + borderWidth,
height: insets.height + borderWidth
)
}
/// Draw the stars in interface builder
#available(iOS 8.0, *)
override public func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
setNeedsDisplay()
}
}
func createBadge(_ who: BadgeSwift, _ view: UIView, _ value: String) {
if !view.subviews.contains(who) {
view.addSubview(who)
}
configureBadge(who, view, value)
positionBadge(who, view)
}
func removeBadge(_ who: BadgeSwift, _ view: UIView) {
if view.subviews.contains(who) {
who.removeFromSuperview()
}
}
func configureBadge(_ badge: BadgeSwift, _ view: UIView, _ value: String) {
badge.text = value
badge.insets = CGSize(width: 2, height: 2)
badge.font = UIFont.boldSystemFont(ofSize: 12)
badge.textColor = Colors.white
badge.badgeColor = Colors.badgeRed
}
func positionBadge(_ badge: UIView, _ view: UIView) {
badge.translatesAutoresizingMaskIntoConstraints = false
var constraints = [NSLayoutConstraint]()
constraints.append(NSLayoutConstraint(
item: badge,
attribute: NSLayoutAttribute.centerY,
relatedBy: NSLayoutRelation.equal,
toItem: view,
attribute: NSLayoutAttribute.top,
multiplier: 1, constant: 5)
)
constraints.append(NSLayoutConstraint(
item: badge,
attribute: NSLayoutAttribute.centerX,
relatedBy: NSLayoutRelation.equal,
toItem: view,
attribute: NSLayoutAttribute.right,
multiplier: 1, constant: -5)
)
view.addConstraints(constraints)
}
func calculateCount(_ items: [UITabBarItem]) -> String {
var countInt = 0
for i in items {
if let countString = i.badgeValue {
countInt = countInt + Int(countString)!
}
}
return String(countInt)
}
For your purpose to create a Badge inside a UITableViewCell, you could use:
let badge = BadgeSwift()
createBadge(badge, MyCell, "10")
That would give you a Badge of 10.

AVPlayerViewController play/pause not working in tvOS

I am working on tvOS app .I have a list of videos and before play main video need to play DFP ad with skip functionality.
I have added skip button on the AVPlayerViewController's view and manage focus(move focus skip button to AVPlayerViewController's view). Focus is working fine if i added AVPlayerViewController as a childViewController but in main video play/pause functionality not working.
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
If i have added AVPlayerViewController as ParentViewController then focus is not working.
self.view.addSubview(playerController.view)
playerController.didMoveToParentViewController(self)
I have implemented blow code
let playerController = AVPlayerViewController()
var playerObj:AVPlayer!
var asset:AVAsset!
var playerItem:AVPlayerItem!
override func viewDidLoad() {
super.viewDidLoad()
playerController.view.frame = CGRectMake(0.0, 0.0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
self.preferredFocusedView
self.updateFocusIfNeeded()
}
func playVideo(videoURL:String) {
self.playerController.showsPlaybackControls = false
self.asset = AVAsset(URL: NSURL(string: videoURL)!) as AVAsset
self.playerItem = AVPlayerItem(asset: self.asset)
self.playerObj = AVPlayer(playerItem: self.playerItem)
self.playerController.player = self.playerObj
self.playerItem.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.New, context: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(VideoPlayerViewController.playerItemDidReachEnd(_:)), name: AVPlayerItemDidPlayToEndTimeNotification, object: self.playerObj.currentItem)
if !self.controlFlag{
// For main video
playFlag = true
self.playerController.showsPlaybackControls = true
}else{
// for DFP ad
playFlag = false
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(VideoPlayerViewController.playerStalledNotification(_:)), name: AVPlayerItemPlaybackStalledNotification, object: self.playerObj.currentItem)
}
self.playerController.player!.play()
}
override var preferredFocusedView: UIView? {
get {
if skipAdsButton != nil {
print("focus on skip button")
return self.skipAdsButton
}else{
print("focus on super.preferredFocusedView")
return super.preferredFocusedView
}
}
}
How to fix this issue. Please suggest it.

Swift NSTimer only runs function once

So I have looked around, and I can't find what I am doing wrong, I am running Swift 1.2, and using SpriteKit by the way.
//showing you just in case
import SpriteKit
//My variables
var countDownLabel = SKLabelNode(fontNamed: "AmericanTypewriter-Light")
var time = 4
var timer = NSTimer()
//Don't want to give you my entire project, I know I can't override a function outside of its class
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if playButton.containsPoint(location) {
//I want this to run 4 times, but is only running once
func countdown() {
time--
countDownLabel.text = "\(time)"
}
//This is set to run my function every second for ever.
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector(countdown()), userInfo: nil, repeats: true)
playButton.removeFromParent()
countDownLabel.fontColor = SKColor.blackColor()
countDownLabel.fontSize = 70
self.addChild(countDownLabel)
}
}
I want my function countdown to run 4 times, but It is only running once for some reason, even though its set to run forever, I am also not getting any error or warnings, sorry if obvious, and if code is hard to understand, I just included the function it was all in.
'Selector(countdown())' is evaluated to nil. So,
1.Move 'countdown' function to outside of touchesBegin.
2.Use 'selector: "countdown"' instead of 'Selector(countdown())'
3.Invalidate your timer after called four times.
var time = 4
var timer = NSTimer()
func countdown() {
if --time == 0 { timer.invalidate() }
println( "\(time)" )
}
func X() {
timer = NSTimer.scheduledTimerWithTimeInterval( 1, target: self, selector: "countdown", userInfo: nil, repeats: true)
}