I have this function and variable:
var trianglePlacement: CGFloat
func setPlacmentOfTriangle(trianglePlacementVal: CGFloat) {
trianglePlacement = trianglePlacementVal
}
I use the function later to do a calculation for this variable:
var trianglePosition: CGFloat = setPlacmentOfTriangle() * width
I am getting a Missing argument for parameter #1 in call error, okay makes sense, I am not passing in an argument.
Is there are way to get rid of the error without getting rid of the argument and not passing in an argument? These are the only times they show up in my code.
In case you are curious, this is the variable width:
var width: CGFloat = self.menubar.frame.size.width
Yes, there is a way you can call the function without passing in an argument. You can provide a default value to the function parameter like so:
func setPlacmentOfTriangle(trianglePlacementVal: CGFloat = 1.0) {
trianglePlacement = trianglePlacementVal
}
Then you call it like this:
setPlacmentOfTriangle()
If you call the setPlacmentOfTriangle function, it will use a value of 1.0 anytime you don't provide your own argument.
I would guess that this is a simplified example, but this function is not necessary at all. You really should just set the value of trianglePlacement directly, and not use a function like you are. Also, this line won't work:
var trianglePosition: CGFloat = setPlacmentOfTriangle() * width
The setPlacmentOfTriangle function doesn't return a value, so there is nothing to multiply with width.
// This is not very functional, avoid mutating other scopes
var trianglePlacement: CGFloat = 0
func setPlacmentOfTriangle(trianglePlacementVal: CGFloat) {
trianglePlacement = trianglePlacementVal
}
// this is better
var trianglePlacement: CGFloat = 0
func setPlacmentOfTriangle(trianglePlacementVal trianglePlacementVal: CGFloat) -> CGFloat {
return trianglePlacementVal
}
// this is a function with a default param value
var trianglePlacement: CGFloat = 0
func setPlacmentOfTriangle(trianglePlacementVal trianglePlacementVal: CGFloat = 0.0) -> CGFloat {
return trianglePlacementVal
}
This is because setPlacmentOfTriangle should take in a CGFloat as a parameter and you're not sending it one
If you do not always want to pass an argument then you can make it optional like so:
func setPlacmentOfTriangle(trianglePlacementVal: CGFloat?) {
if let tri = trianglePlacementVal as? CGFloat {
trianglePlacement = tri
}
}
Then you can pass nil as your parameter:
setPlacmentOfTriangle(nil)
Related
I'm trying to use CoreGraphics to draw some musical notes from a struct that is defined elsewhere in a swiftui App
func drawSheetMusic(in size: CGSize) -> some View {
return ZStack {
Color.clear.drawingGroup { ctx in
for note in self.musicData.notes {
let rect = CGRect(x: note.position.x - note.radius, y: note.position.y - note.radius, width: 2 * note.radius, height: 2 * note.radius)
ctx.cgContext.addEllipse(in: rect)
ctx.cgContext.setFillColor(Color.black.cgColor)
ctx.cgContext.fillPath()
}
return Rectangle().fill(Color.clear)
}
}
}
But this is returning an error Type of expression is ambiguous without more context on Line 2 there. What exactly am I doing wrong here?
func drawingGroup(opaque: Bool = false, colorMode: ColorRenderingMode = .nonLinear) -> some View
doesn't take a closure parameter. The problem becomes obvious if you remove the ZStack.
You seem to be confusing this with
UIGraphicsImageRenderer.image(actions: (UIGraphicsImageRendererContext) -> Void) -> UIImage
Is there a way for swift code to check if this variable changes and than change the shape of an object?
var Shape = 0.4
let a = (self.size.width + self.size.height) * Shape
Using property observers you can achieve what you are looking for. didSet will be called after varibale value changed.
var variable1 = 0 {
didSet {
onValueChange()
}
}
func onValueChange() {
print("Change \(variable1)")
}
To apply a gradient to a NSView, it seems best to make a class that inherits from NSView and override the draw method that sets a path and draws the gradient.
class GradientView: NSView {
var startColor = NSColor.red
var startPosition: CGFloat = 0
var endColor = NSColor.white
var endPosition: CGFloat = 1
var rotation: CGFloat = 0
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let bgGradient = NSGradient(colorsAndLocations: (startColor, startPosition), (endColor, endPosition))
let path = NSBezierPath.init(rect: self.bounds)
bgGradient?.draw(in: path, angle: rotation)
}
}
(Let me know if there's a better/easier way...)
It works great, but now I want to send a custom collection of colors & color-stops. Note: *With this above method, I am limited to defining two (the startColor & startPosition and endColor & endPosition) only.
I'd like to use (an array ? of) tuples (containing several colors and their stop values). The API to set a color and location is NSGradient(colorsAndLocations:) and uses the tuple (NSColor, CGFloat).
I can add a parameter like var colorStopArray: [(color: NSColor, stop: CGFloat)] = [] that creates an array of (such) tuples. Also I can append values with: colorStopArray.append((startColor, startPosition)).
But how can I assign this to the API?
I've tried many methods and they all cause errors (eg. tried NSGradient(colorsAndLocations: colorStopArray.flatMap{return ($0.color, $0.stop)}) )
So how can send many custom colors+locations to the above class (or hopefully a better suggestion) to create a custom gradient (which will then be drawn on my NSView)?
With this above method, I am limited to defining two (the startColor & startPosition and endColor & endPosition) only.
No, you're not. This is a variadic; you can add as many tuples as you wish. This works fine:
let startColor = NSColor.red
let startPosition: CGFloat = 0
let middleColor = NSColor.blue
let middlePosition: CGFloat = 0.5
var endColor = NSColor.white
var endPosition: CGFloat = 1
let bgGradient = NSGradient(colorsAndLocations:
(startColor, startPosition),
(middleColor, middlePosition),
(endColor, endPosition))
If the question is "can I turn an array into a variadic?", then the answer is, unfortunately no. This feature ("splatting") is missing from the Swift language. The only way to call a variadic in Swift is as a variadic.
The initializer that lets you supply an array of colors and stop locations is init(colors:atLocations:colorSpace:). So if that's what you want to supply, call that initializer instead.
Using matt's clues, I replaced the let line (using the more appropriate API init(colors:atLocations:colorSpace:)) :
let bgGradient = NSGradient(colorsAndLocations: (startColor, startPosition), (endColor, endPosition))
with
let bgGradient = NSGradient(colors: colorStopArray.map { $0.color }, atLocations: colorStopArray.map { $0.stop }, colorSpace: NSColorSpace.genericRGB)
Now it functions as I desired.
In Objective-C, we can declare a function like this:
- (void)getRect:(CGRect *)aRectRef bRect:(CGRect *)bRectRef
{
if (aRectRef) *aRectRef = CGRectZero
if (bRectRef) *bRectRef = CGRectZero
}
and pass NULL to the function:
CGRect rect;
[self getRect:NULL bRect:rect]
There isn't NULL in Swift. I can't use nil as inout param directly either:
func getRect(aRect aRectRef: inout CGRect?, bRect bRectRef: inout CGRect?) -> Void {
...
}
self.getRect(&nil, bRect: rect) // <- ERROR
I must define a variable with nil value and pass it to the function, even though I don't need the variable totally.
How to pass nil to the function?
UPDATE:
null / nil in swift language just explained nil in Swift.
Swift optional inout parameters and nil explained how to define a variable with nil value and pass it as inout parameter.
I want to know there is a way to pass nil directly like &nil to function or not.
Your Objective-C method has nullable pointers as parameters,
in Swift 3 that would be an optional UnsafeMutablePointer:
func getRect(aRectRef: UnsafeMutablePointer<CGRect>?, bRectRef: UnsafeMutablePointer<CGRect>?) {
if let aRectPtr = aRectRef {
aRectPtr.pointee = CGRect(x: 1, y: 2, width: 3, height: 4)
}
if let bRectPtr = bRectRef {
bRectPtr.pointee = CGRect(x: 5, y: 6, width: 7, height: 8)
}
}
var rect = CGRect.zero
getRect(aRectRef: &rect, bRectRef: nil)
print(rect) // (1.0, 2.0, 3.0, 4.0)
So you can pass nil as an argument. What you can not do
(in contrast to Objective-C) is to pass the address of an uninitialized variable, rect must be initialized here.
The same can be written more compactly as
func getRect(aRectRef: UnsafeMutablePointer<CGRect>?, bRectRef: UnsafeMutablePointer<CGRect>?) {
aRectRef.map { $0.pointee = CGRect(x: 1, y: 2, width: 3, height: 4) }
bRectRef.map { $0.pointee = CGRect(x: 5, y: 6, width: 7, height: 8) }
}
Sorry, I am not allowed to add comment at this time, so I'll write this as an answer. In SWIFT, you defined the parameter as inout, you have to pass in a variable and not literal nil. You can do something like this,
func testGetRect()
{
var recta: CGRect? = nil
var rectb: CGRect? = CGRect()
self.getRect(aRect: &recta, bRect: &rectb)
}
func getRect(inout aRect aRectRef: CGRect?, inout bRect bRectRef: CGRect?) -> Void
{
if (aRectRef != nil)
{
aRectRef = CGRectZero
}
if (bRectRef != nil)
{
bRectRef = CGRectZero
}
}
Yes, you "must define a variable with nil and pass it to the function. I tried some casting to see if it work, but couldn't. The parameter passing with inout is like C++ parameter passing by reference, i.e foo(int ¶ma, int ¶mb). You must pass a variables to foo. I don't believe SWIFT has parameter passing like the obj-c example that you have.
I'm trying to write a class which allows me to easily interpolate between two values.
class Interpolation
{
class func interpolate<T>(from: T, to: T, progress: CGFloat) -> T
{
// Safety
assert(progress >= 0 && progress <= 1, "Invalid progress value: \(progress)")
if let a = from as? CGFloat, let b = to as? CGFloat
{
}
if let a = from as? CGPoint, let b = to as? CGPoint
{
}
if let from = from as? CGRect, let to = to as? CGRect
{
var returnRect = CGRect()
returnRect.origin.x = from.origin.x + (to.origin.x-from.origin.x) * progress
returnRect.origin.y = from.origin.y + (to.origin.y-from.origin.y) * progress
returnRect.size.width = from.size.width + (to.size.width-from.size.width) * progress
returnRect.size.height = from.size.height + (to.size.height-from.size.height) * progress
return returnRect // Cannot convert return expression of type 'CGRect' to return type 'T'
}
return from
}
}
Unfortunately, it gives me an error at return returnRect: Cannot convert return expression of type 'CGRect' to return type 'T'. Maybe I'm not understanding how generics are used...I just want to have one function that will handle interpolating between various types, rather than having a bunch of functions like func interpolate(from: Int, to: Int), func interpolate(from: CGPoint, to: CGPoint), etc.
The problem is that T is a generic placeholder – meaning that you cannot know what the actual concrete type of T is from within the function. Therefore although you are able to conditionally cast from and to to a CGRect (thus establishing that T == CGRect), Swift is unable to infer this information and therefore prohibits attempting to return a CGRect when it expects a return of T.
The crude solution therefore is to force cast the return result back to T in order to bridge this gap in information with the type-system:
if let from = from as? CGRect, let to = to as? CGRect {
// ...
return returnRect as! T
}
However, this kind of type-casting is really a sign that you're fighting the type-system and not taking advantage of the static typing that generics offer, and therefore is not recommended.
The better solution, as #Wongzigii has already said, is to use a protocol. For example, if you define an Interpolate protocol as he shows in his answer – you can then use this protocol in order to constrain your generic placeholder T in your interpolate function:
class Interpolation {
class func interpolate<T:Interpolate>(from: T, to: T, progress: CGFloat) -> T {
// Safety
assert(progress >= 0 && progress <= 1, "Invalid progress value: \(progress)")
return T.interpolate(from: from, to: to, progress: progress)
}
}
This solves many of your problems – it does away with the runtime type-casting and instead uses the protocol constraint in order to call the specialised interpolate function. The protocol constraint also prevents you from passing any types that don't conform to Interpolate at compile-time, and therefore also solves the problem of what to do when your type-casting fails.
Although that being said, I actually quite like the solution that #JoshCaswell suggested in his answer to your other question – overloading operators in order to achieve this functionality. As with the previous solution, the key is to define a protocol that encapsulates the functionality you're defining on each type, and then constrain your generic function to this protocol.
A simple implementation may look like this:
protocol Interpolatable {
func +(lhs:Self, rhs:Self) -> Self
func -(lhs:Self, rhs:Self) -> Self
func *(lhs:Self, rhs:CGFloat) -> Self
}
func +(lhs:CGRect, rhs:CGRect) -> CGRect {
return CGRect(x: lhs.origin.x+rhs.origin.x,
y: lhs.origin.y+rhs.origin.y,
width: lhs.size.width+rhs.size.width,
height: lhs.size.height+rhs.size.height)
}
func -(lhs:CGRect, rhs:CGRect) -> CGRect {
return CGRect(x: lhs.origin.x-rhs.origin.x,
y: lhs.origin.y-rhs.origin.y,
width: lhs.size.width-rhs.size.width,
height: lhs.size.height-rhs.size.height)
}
func *(lhs:CGRect, rhs:CGFloat) -> CGRect {
return CGRect(x: lhs.origin.x*rhs,
y: lhs.origin.y*rhs,
width: lhs.size.width*rhs,
height: lhs.size.height*rhs)
}
extension CGRect : Interpolatable {}
extension CGFloat : Interpolatable {}
class Interpolation {
class func interpolate<T:Interpolatable>(from: T, to: T, progress: CGFloat) -> T {
assert(progress >= 0 && progress <= 1, "Invalid progress value: \(progress)")
return from + (to - from) * progress
}
}
It would be nice if you use Protocol to scale your generic type.
protocol Interpolate {
associatedtype T
static func interpolate(from: T, to: T, progress: CGFloat) -> T
}
Then let CGRect extension conform to your protocol:
extension CGRect: Interpolate {
typealias T = CGRect
static func interpolate(from: T, to: T, progress: CGFloat) -> CGRect.T {
var returnRect = CGRect()
returnRect.origin.x = from.origin.x + (to.origin.x-from.origin.x) * progress
returnRect.origin.y = from.origin.y + (to.origin.y-from.origin.y) * progress
returnRect.size.width = from.size.width + (to.size.width-from.size.width) * progress
returnRect.size.height = from.size.height + (to.size.height-from.size.height) * progress
return returnRect
}
}
var from = CGRect(x: 0, y: 0, width: 1, height: 1) // (0, 0, 1, 1)
var to = CGRect(x: 1, y: 1, width: 0, height: 0) // (1, 1, 0, 0)
CGRect.interpolate(from, to: to, progress: 1) // (1, 1, 0, 0)
Also, this would make NSString conform to protocol Interpolate easily, like:
extension NSString: Interpolate {
typealias T = NSString
static func interpolate(from: T, to: T, progress: CGFloat) -> NSString.T {
//...
return ""
}
}