Trying to switch a direct integer value with a variable in swift - objective-c

I'm trying to switch out a direct integer with a variable in swift, but for some reason I'm getting this error and I have no idea. The end goal is to get my currentValue (line 76) to replace the 100's on line 41 - could anyone let me know how I could accomplish this without the error? New to swift and having a hard time (background in objective-c, figured something this simple would not stop me in my tracks!)
Full .swift file here: http://pastebin.com/K6UHkNEv
EDIT:
// these values change the number of squares
let _gameView = CGOLView(gridWidth:100, gridHeight:100)
#IBOutlet weak var tileSizeSlider: UISlider!
#IBAction func sliderValueChanged(sender: UISlider) {
var currentValue = Int(sender.value)
print("\(currentValue)")
}
should work as:
// these values change the number of squares
let _gameView = CGOLView(gridWidth:currentValue, gridHeight:currentValue)
#IBOutlet weak var tileSizeSlider: UISlider!
#IBAction func sliderValueChanged(sender: UISlider) {
var currentValue = Int(sender.value)
print("\(currentValue)")
}
instead I get this error:
Use of unresolved identifier 'currentValue'
and if I try to create custom int's and input them:
var gridWidthValue = 50
var gridHeightValue = 50
like this:
let _gameView = CGOLView(gridWidth:gridWidthValue, gridHeight:gridHeightValue)
I get:
'ViewController.Type' does not have a member named 'gridHeightValue'
Any help would be appreciated - thanks stackoverflow community!
David.

currentValue is a local variable to sliderValueChanged.
Instead you should instantiate _gameView in init. Note however, you still won't be able to use currentValue.
If this is a one off sort of thing, you can always make _gameView an optional and then create it when you have adjusted the slider. This is admittedly a little clumsy.
I am not familiar with Conway's Game of Life, but looking at the code, it seems CGOLView's init does some adjustment based on the grid width and height. The reason I am mentioning this is that you could always change the view's frame size, however, you'd then also need to make some other mods to the tileViews for it to look proper.
As to why gridWidthValue/gridHeightValue is not working. Those are properties defined in an instance. Hence you would need to do somethign like self.gridWithValue to reference it. However, you cannot do that when defining the property such as
let _gameView = CGOLView(gridWidth:gridWidthValue, gridHeight:gridHeightValue)
This is also why instantiating _gameView in init is the way to go.

Your problem is that you cannot access the variable currentValue because it is inside of a function. You have to declare that value outside of the function to be able to use it outside of the function.

Related

Godot/GDScript Grid Movement Tweening

So I've been using KidsCanCode's Godot tutorials/documentation to help me create a Pokemon-like grid-based movement for a project I'm working on. For all intents and purposes, I would like to create a movement system as close to that in the earlier handheld Pokemon games as possible.
I would like to add two things before I start; one, I have grown fond of the way KidsCanCode attempted to teach grid-based movement, so while other ways of coding it may be simpler such as those that can be found on videos such as this one (https://www.youtube.com/watch?v=jSv5sGpnFso), I would like to hard-headidly stick to this method of coding it... you'll see what I mean when you read the code. Lastly, I would like to add that I had this code working before ! I actually haven't made any changes to the code since it was last working, however, for some reason it no longer seems to work, I'm not sure if that's due to Godot updating since, but hopefully someone can help me out with that.
So first of all, this is my player scene node tree. The most important parts of this being the RayCast2D and Tween nodes.
And this is my code for the main Area2D Player node:
extends Area2D
const tile_size = 16
export var speed = 5
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
func _ready():
position = position.snapped(Vector2.ONE * tile_size/2)
func _unhandled_input(event):
if $Tween.is_active():
return
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(inputs[dir])
func move(dir):
$RayCast2D.cast_to = inputs[dir] * tile_size
$RayCast2D.force_raycast_update()
if !$RayCast2D.is_colliding():
move_tween(dir)
func move_tween(dir):
$Tween.interpolate_property(self, "position", position,
position + inputs[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
$Tween.start()
To quickly explain, func _ready(): snaps the player to the grid. func _unhandled_input(event): then checks to see if a Tween is occurring, and if not, calls func move(dir). This function raycasts to the given direction input, forces a raycast update, and if no static body is in the given direction, calls func move_tween(dir). This last functions handles tween interpolation to the given direction and starts the tweening process. That's pretty much it. Once again, this used to work just fine.
However, now when I try to run this, I get an error "Invalid get index '(0, 1)' (on base: 'Dictionary')" where "(0, 1)" changes based on what direction I tried to move in when the game was running.
In the Debugger dock, underneath Stack Frames, it gives me errors on lines "22 - at function; move" $RayCast2D.cast_to = inputs[dir] * tile_size and "19 - at function: _unhandled_input" move(inputs[dir]).
The code on the website had these say (dir) only instead of (inputs[dir]). But doing so only gives me another error. If anyone smarter than me has any idea what's going on, I would very much appreciate any and all insight. Thank you !
Understanding the problem
Alright, let us see. The variable inputs has your dictionary:
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
The keys are String, and the values are Vector2.
Thus, here:
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(inputs[dir])
The variable dir is going to be a String. Which is what you need for is_action_pressed, so that is correct.
And inputs[dir] is going to be a Vector2. Which means that in move you are getting a Vector2 as argument.
Now, in move you say::
func move(dir):
$RayCast2D.cast_to = inputs[dir] * tile_size
But remember that the argument you are passing is a Vector2, and the keys of input are all String. So it fails here: inputs[dir].
Early warning for similar problems
Using types can help you identify this kind of problems early. Sadly in Godot 3.x there is no way to specify the the keys and values of a Dictionary.
Arguably you could use C# and use .NET Dictionary<TKey,TValue> from the System.Collections.Generic, which would let you specify the key and value types. Yet, we are not talking about those dictionaries here.
What you can tell with GDScript is that your parameters are either Vector2:
func move(displacement:Vector2):
# …
Or String
func move(dir:String):
# …
This way Godot can tell you when you are calling them with the wrong parameter.
Another thing that will help. Although it is more on the discipline side, is to keep consistent names. If the names you use have a concrete meaning in your system, they will help you.
For instance, you call move like this:
move(inputs[dir])
Meaning that what you are passing is not called dir※. But you have move defined like this:
func move(dir):
# …
So move expects something you call a dir. And you would see that when you are typing the call to move.
※: I'd say you are passing one of the values of inputs, so what you are passing is called an input. Or you could call them action, given that you use them in is_action_pressed. Which, again, would be using names in a way that helps you.
Solving the problem
The way I would solve this is by using the String and inputs in _unhandled_input only (after all, that function is meant to deal with inputs). And work with Vector2 from there on. This means that:
The other methods would also be useful if in the future you wanted a movement that does not come from one of the inputs.
You are not repeating the effort of looking up in the Dictionary.
Admittedly, these aren't a huge deal for your game right now. And ultimately what you do is up to you. Yet, consider this approach submitted to your consideration.
This is the code (I have added some type annotations):
extends Area2D
const tile_size:float = 16
export var speed:float = 5
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
func _ready():
position = position.snapped(Vector2.ONE * tile_size/2)
func _unhandled_input(event:InputEvent) -> void:
if $Tween.is_active():
return
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(inputs[dir])
func move(displacement:Vector2) -> void:
$RayCast2D.cast_to = displacement * tile_size
$RayCast2D.force_raycast_update()
if !$RayCast2D.is_colliding():
move_tween(displacement)
func move_tween(displacement:Vector2) -> void:
$Tween.interpolate_property(self, "position", position,
position + displacement * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
$Tween.start()
Or you can using String thought out, and querying the dictionary every time. Which, I believe, is what you intended. Like this:
extends Area2D
const tile_size:float = 16
export var speed:float = 5
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
func _ready():
position = position.snapped(Vector2.ONE * tile_size/2)
func _unhandled_input(event:InputEvent) -> void:
if $Tween.is_active():
return
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(dir)
func move(dir:String) -> void:
$RayCast2D.cast_to = input[dir] * tile_size
$RayCast2D.force_raycast_update()
if !$RayCast2D.is_colliding():
move_tween(dir)
func move_tween(dir:String) -> void:
$Tween.interpolate_property(self, "position", position,
position + input[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
$Tween.start()
Notice here that _unhandled_input is passing dir to move. The same way that move is passing dir to move_tween.

Passing custom parameters in render function

I have below code to create column:
DTColumnBuilder.newColumn(null).withTitle('Validation').renderWith(validationRenderer)
and render function:
function validationRenderer(data, type, full, meta) {
.......
}
Now, I want to pass custom parameters to validationRenderer so that I can access it inside the function, like below:
DTColumnBuilder.newColumn(null).withTitle('Validation').renderWith(validationRenderer('abc'))
function validationRenderer(data, type, full, meta, additionalParam) {
// do something with additionalParam
}
I could not find it in the documentation but there must be something to pass additional parameters in meta as per the reference from here
Yes, you can. Or, better, you technically can, but you may use a clever workaround to handle your issue.
I had this issue today, and found a pretty sad (but working) solution.
Basically, the big problem is that the render function is a parameter passed to the datatable handler, which is (of course) isolated.
In my case, to make a pratical example, I had to add several dynamic buttons, each with a different action, to a dynamic datatable.
Apparently, there was no solution, until I thought the following: the problem seems to be that the renderer function scope is somewhat isolated and unaccessible. However, since the "return" of the function is called only when the datatable effectively renders the field, you may wrap the render function in a custom self-invoking-anonymous-function, providing arguments there to use them once the cell is being rendered.
Here is what I did with my practical example, considering the following points:
The goal was to pass the ID field of each row to several different custom functions, so the problem was passing the ID of the button to call when the button is effectively clicked (since you can't get any external reference of it when it is rendered).
I'm using a custom class, which is the following:
hxDatatableDynamicButton = function(label, onClick, classNames) {
this.label = label;
this.onClick = onClick;
this.classNames = this.classNames || 'col5p text-center';
}
Basically, it just creates an instance that I'm later using.
In this case, consider having an array of 2 different instances of these, one having a "test" label, and the other one having a "test2" label.
I'm injecting these instances through a for loop, hence I need to pass the "i" to my datatable to know which of the buttons is being pressed.
Since the code is actually quite big (the codebase is huge), here is the relevant snippet that you need to accomplish the trick:
scope.datatableAdditionalActionButtons.reverse();
scope._abstractDynamicClick = function(id, localReferenceID) {
scope.datatableAdditionalActionButtons[localReferenceID].onClick.call(null, id);
};
for (var i = 0; i < scope.datatableAdditionalActionButtons.length; i++) {
var _localReference = scope.datatableAdditionalActionButtons[i];
var hax = (function(i){
var _tmp = function (data, type, full, meta) {
var _label = scope.datatableAdditionalActionButtons[i].label;
return '<button class="btn btn-default" ng-click="_abstractDynamicClick('+full.id+', '+i+')">'+_label+'</button>';
}
return _tmp;
})(i);
dtColumns.unshift(DTColumnBuilder.newColumn(null).notSortable().renderWith(hax).withClass(_localReference.classNames));
}
So, where is the trick? the trick is entirely in the hax function, and here is why it works: instead of passing the regular renderWith function prototype, we are using a "custom" render, which has the same arguments (hence same parameters) as the default one. However, it is isolated in a self invoking anonymous function, which allows us to arbitrarely inject a parameter inside it and, so, allows us to distinguish, when rendering, which "i" it effectively is, since the isolated scope of the function is never lost in this case.
Basically, the output is as follow:
And the inspection actually shows that elements are effectively rendered differently, hence each "i" is being rendered properly, while it wouldn't have if the function wouldn't have been wrapped in a self invoking anonymous function:
So, basically, in your case, you would do something like this:
var _myValidator = (function(myAbcParam){
var _validate = function (data, type, full, meta) {
console.log("additional param is: ", myAbcParam); // logs "abc"
return '<button id="'+myAbcParam+'">Hello!</button>'; // <-- renders id ="abc"
}
return _validate ;
})('abc');
DTColumnBuilder.newColumn(null).withTitle('Validation').renderWith(_myValidator);
// <-- note that _myValidator is passed instead of "_myValidator()", since it is already executed and already returns a function.
I know this is not exactly the answer someone may be expecting, but if you need to accomplish something that complex in datatable it really looks like the only possible way to do this is using a self invoking anonymous function.
Hope this helps someone who is still having issues with this.

In Swift, can you pass nil as an input to a function?

I'm reading many articles about how you shouldn't check an object for nil. It's a objC paradigm and it's a bad design and w/ swift it's been eliminated. So my question is, per example below, can you pass thru "group" as nil value? does the nil-checking mechanism happen when the function is called, hence removing the need to implement if(group==nil){..} ?
func deleteMembershipForGroup(group:GroupData){
}
You need to use an optional:
func deleteMembershipForGroup(group:GroupData?){
if let groupReal = group {
// not nil
}
}
Yes! Thomas Kilian is right and it works for me! You will then be able to pass a nil parameter. You will also notice that using optional variable, it will also removed the warning saying the variable "group" will always be true.
func deleteMembershipForGroup(group:GroupData?){
if let groupReal = group { <--- Warning gone!
// not nil
}
}

MKPolygon using Swift (Missing argument for parameter 'interiorPolygons' in call)

Fellow Devs,
I'm trying to implement a polygon overlay on a mapview as follows:
private func drawOverlayForObject(object: MyStruct) {
if let coordinates: [CLLocationCoordinate2D] = object.geometry?.coordinates {
let polygon = MKPolygon(coordinates: coordinates, count: coordinates.count)
self.mapView.addOverlay(polygon)
}
}
The following error is presented:
Missing argument for parameter 'interiorPolygons' in call
According to the documentation:
Apple Docu:
Mutable Pointers
When a function is declared as taking an UnsafeMutablePointer
argument, it can accept any of the following:
nil, which is passed as a null pointer
An UnsafeMutablePointer value
An in-out expression whose operand is a stored lvalue of type Type, which is passed as the address of the lvalue
An in-out [Type] value, which is passed as a pointer to the start of the array, and lifetime-extended for the duration of the call
Now I think that my approach then would be correct, providing a [CLLocationCoordinate2D] array. Did anyone experience the same problem and found a workaround?
thanks
Ronny
The error you're getting is Swift's cryptic way of saying that it can't find a method which matches your parameters. If you did try passing the interiorPolygons parameter, you'd get an equally confusing:
Extra argument 'interiorPolygons' in call
Your code is pretty close though; you just need a couple of minor changes. In the doc you reference, it says one of the things you can pass is:
An in-out [Type] value, which is passed as a pointer to the start of
the array, and lifetime-extended for the duration of the call
So, it's looking for an in-out parameter. Which is done by passing coordinates prefixed with an &, like so:
MKPolygon(coordinates: &coordinates, count: coordinates.count)
But, in-out parameters can't be constants. From the docs:
You can only pass a variable as the argument for an in-out parameter.
You cannot pass a constant or a literal value as the argument, because
constants and literals cannot be modified.
So, you need to define coordinates with a var first:
if var coordinates: [CLLocationCoordinate2D] = object.geometry?.coordinates
Which makes the entire function look like this:
private func drawOverlayForObject(object: MyStruct) {
if var coordinates: [CLLocationCoordinate2D] = object.geometry?.coordinates {
let polygon = MKPolygon(coordinates: &coordinates, count: coordinates.count)
self.mapView.addOverlay(polygon)
}
}
My final solution by cherry-picking from several tutorials and integrating:
func setPolylineFromPoints(locations:[CLLocation]){
if locations.count == 0 {
return;
}
// while we create the route points, we will also be calculating the bounding box of our route
// so we can easily zoom in on it.
var pt : UnsafeMutablePointer<MKMapPoint>? // Optional
pt = UnsafeMutablePointer.alloc(locations.count)
for idx in 0..<locations.count-1 {
let location = locations[idx]
let point = MKMapPointForCoordinate(location.coordinate);
pt![idx] = point;
}
self.polyline = MKPolyline(points:pt!, count:locations.count-1)
// clear the memory allocated earlier for the points
pt?.destroy()
pt?.dealloc(locations.count)
}

CMutablePointer<NSRange> dead end

I'm trying to call lineFragmentRectForGlyphAtIndex method of NSLayoutManager object in Swift. Its description is that:
func lineFragmentRectForGlyphAtIndex(glyphIndex: Int, effectiveRange: CMutablePointer<NSRange>)
But I can't figure it out how am I suppose to call it. According to Apple docs I have to call it like this:
let someRange : NSRange = ...
layoutManager.lineFragmentRectForGlyphAtIndex(0, effectiveRange: &someRange)
That's what documentation says. But I get an error: 'NSRange' is not a subtype of '#lvalue $T5'
What should I do?
Try making it a var instead of a let:
var someRange: NSRange = ...
layoutManager.lineFragmentRectForGlyphAtIndex(0, effectiveRange: &someRange)
Basically: using the & on a var gives you something compatible with a CMutablePointer, but a let is not supposed to be mutable. Hence the type error.
If you want to pass a null pointer, just write nil instead of &someRange (and then of course you don't need to declare the extra var).