I have this call:
myObject.perform(Selector("setCellSize:"), with: CGSize(width: 50.0, height: 50.0))
and inside class I have:
func setCellSize(_ size: CGSize) {
print(size)
self.itemSize = size
}
The method is correctly called, but it prints (0.0, 7.2911220195564e-304). What is wrong?
perform(_:with:) is a method from the NSObjectProtocol and
Sends a message to the receiver with an object as the argument.
In particular,
aSelector should identify a method that takes a single argument of type id. For methods with other argument types and return values, use NSInvocation.
If you really have to pass a CGSize via this method then you can
wrap it into a NSValue:
let value = NSValue(cgSize: CGSize(width: 50.0, height: 50.0))
myObject.perform(#selector(setCellSize(_:)), with: value)
func setCellSize(_ size: NSValue) {
print(size.cgSizeValue)
}
CGSize is not an object. It's a struct. You're printing gibberish passed to your setter. The function is only for objects.
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
I've reviewed the other questions on this topic on SO (there are many) but none of them address this error pertaining to a #State . And I thought that this was the very problem that #State solved!
Here is the relevant code
struct ViewA: View {
#State private var startAnimation = false
let width: CGFloat
let height: CGFloat
init(width: CGFloat, height: CGFloat) {
self.width = width
self.height = height
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
startAnimation = true //<--- The error is on this line
}
}
var body: some View {
Circle()
.frame(width: width, height: height)
.scaleEffect(CGSize(width: startAnimation ? 5.0 : 1.0 , height: startAnimation ? 5.0 : 1.0))
}
}
There error is Escaping closure captures mutating 'self' parameter
Now reviewing the other SO questions related to this error lets me know that this error is related to time delayed changes ...
I don't think it has anything to do with the #State property, but with the fact that you are using an #escaping closure. An escaping closure can cause a strong reference cycle if you use self inside the closure. See for a nice article explaining #escaping closures this link.
Kind regards,
MacUserT
TL;DR:
I want a protocol to provide default init behavior, but the compiler resists adopters adding more stored properties. I solved this with composition instead of inheritance, but what's wrong with my original approach?
Motivation
I want to automate the transformation of objects from design specifications to runtime specs. I use the example of scaling a CGSize but the intent is more general than just geometric layout. (IOW e.g. my solution won't be to adopt/reject/rewrite autolayout.)
Code
You can paste this right into a Playground, and it will run correctly.
protocol Transformable {
var size : CGSize { get } // Will be set automatically;
static var DESIGN_SPEC : CGSize { get } // could be any type.
init(size: CGSize) // Extension will require this.
}
// A simple example of transforming.
func transform(_ s: CGSize) -> CGSize {
CGSize(width: s.width/2, height: s.height/2)
}
// Add some default behavior.
// Am I sinning to want to inherit implementation?
extension Transformable {
init() { self.init(size: transform(Self.DESIGN_SPEC)) }
// User gets instance with design already transformed. No muss, fuss.
}
// Adopt the protocol...
struct T : Transformable {
let size: CGSize
static let DESIGN_SPEC = CGSize(width: 10, height: 10)
}
// ...and use it.
let t = T()
t.size // We get (5,5) as expected.
But every Eden must have its snake. I want a Transformable with another property:
struct T2 : Transformable {
// As before.
let size: CGSize
static let DESIGN_SPEC = CGSize(width: 10, height: 10)
let i : Int // This causes all sorts of trouble.
}
Whaa? Type 'T2' does not conform to protocol 'Transformable'
We have lost the synthesized initializer that sets the size member.
So... we put it back:
struct T3 : Transformable {
// As before.
let size: CGSize
static let DESIGN_SPEC = CGSize(width: 10, height: 10)
let i : Int
init(size: CGSize) {
self.size = size
self.i = 0 // But this is a hard-coded value.
}
}
But now our new member is statically determined. So we try adding another initializer:
struct T4 : Transformable {
// As before.
let size: CGSize
static let DESIGN_SPEC = CGSize(width: 10, height: 10)
let i : Int
init(size: CGSize) { self.size = size ; self.i = 0 }
// Try setting 'i':
init(i: Int) {
self.init() // Get the design spec properly transformed.
self.i = i // 'let' property 'i' may not be initialized directly;
} // use "self.init(...)" or "self = ..." instead
}
Declaring i as var shuts the compiler up. But i is immutable, and I want i that way. Explain to me why what I want is so wrong... This page is too small to include all the variations I tried, but perhaps I have missed the simple answer.
I've been writing a game that has a subclass of the SKSpriteNode with some extra functions and variables. I'd like to set some of the variables when the object is created eg
let mySprite = MySubclass (width: 24, height 33)
I'm not sure this is possible which means I'll probably have to call a methos of the subclass to set the vars in a separate stage which is a bit clunky:
let mySprite = MySubclass ()
mySprite.setSize(24, height: 33)
Any ideas how I can do this in a more elegant way?
Many Thanks,
Kw
This is very fundamental OOP. Here is how you do it in Swift:
class MySubClass: SKSpriteNode {
var width: CGFloat // Declare your own properties
var height: CGFloat // ...
init(width: CGFloat, height: CGFloat) {
self.width = width // set up your own properties
self.height = height // ...
super.init() // call up to the super-class's init to set up its properties
}
}
Have you read Apple's book The Swift Programming Language? It's free and clearly covers this...
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.