in Swift1 i could call this:
func selectParagraphAlignment(newAlignment:NSTextAlignment) {
var selectedRange = textView.selectedRange
var newParagraphStyle = NSMutableParagraphStyle()
newParagraphStyle.alignment = newAlignment
var dict = [NSParagraphStyleAttributeName: newParagraphStyle]
textView.textStorage.beginEditing()
textView.textStorage.setAttributes(dict, range: selectedRange)
textView.textStorage.endEditing()
}
In Swift2 i cant use a Distionary in textView.textStorage.setAttributes(). I cant see, what i must use now?
Can u show me the new syntax?
TIA
Related
I have been trying to figure out how to pan/zoom using onMouseDrag, and onMouseDown in paperjs.
The only reference I have seen has been in coffescript, and does not use the paperjs tools.
This took me longer than it should have to figure out.
var toolZoomIn = new paper.Tool();
toolZoomIn.onMouseDrag = function (event) {
var a = event.downPoint.subtract(event.point);
a = a.add(paper.view.center);
paper.view.center = a;
}
you can simplify Sam P's method some more:
var toolPan = new paper.Tool();
toolPan.onMouseDrag = function (event) {
var offset = event.downPoint - event.point;
paper.view.center = paper.view.center + offset;
};
the event object already has a variable with the start point called downPoint.
i have put together a quick sketch to test this.
Unfortunately you can't rely on event.downPoint to get the previous point while you're changing the view transform. You have to save it yourself in view coordinates (as pointed out here by Jürg Lehni, developer of Paper.js).
Here's a version that works (also in this sketch):
let oldPointViewCoords;
function onMouseDown(e) {
oldPointViewCoords = view.projectToView(e.point);
}
function onMouseDrag(e) {
const delta = e.point.subtract(view.viewToProject(oldPointViewCoords));
oldPointViewCoords = view.projectToView(e.point);
view.translate(delta);
}
view.translate(view.center);
new Path.Circle({radius: 100, fillColor: 'red'});
I am trying to take a string which has shape option information and create the shape on my Google Map application.
The string is made by splitting an array that was built from a local text document.
The string appears as:
Circle{center: new google.maps.LatLng(38.041872419557094, -87.6046371459961),radius:5197.017394363823,fillColor: '#000000',strokeWeight: 1,strokeColor: '#000000',map:map};
The function I have to take such string and make the shape appears as:
function loadDrawings(evt)
{
var f = evt.target.files[0];
if (!f)
{
alert("Failed to load file");
}
else if (!f.type.match('text.*'))
{
alert(f.name + " is not a valid text file.");
}
else
{
var r = new FileReader();
r.onload = function (e)
{
var contents = e.target.result;
var drawings = [];
var drawing;
var drawingType;
var shape;
var shapeOptions;
drawings = contents.split(";");
for (i = 0; i < drawings.length - 1; i++) {
drawing = drawings[i].toString();
drawingType = drawing.substr(0, drawing.indexOf('{'));
if (drawingType == "Circle")
{
shapeOptions = drawing.substr(6); //UNIQUE TO CIRCLE
shape = new google.maps.Circle(shapeOptions);
shape.setMap(map);
}
};
}
r.readAsText(f);
}
}
My issue is shapeOptions as a string does not work in the above syntax for creating the Circle. However, if I take the contents of the string, which is:
{center: new google.maps.LatLng(38.041872419557094, -87.6046371459961),radius:5197.017394363823,fillColor: '#000000',strokeWeight: 1,strokeColor: '#000000',map:map}
And directly enter it, the shape appears.
Do I need a certain variable type for my shapeOptions for this to work? I know that the new google.maps. requires (), but I have had no luck creating a variable from my string. Am I missing something here?
Much appreciation for any help!
Your shapeOptions string is a JavaScript object literal, so you can eval() it to get the object:
shapeOptions = eval( '(' + drawing.substr(6) + ')' );
Since it has map:map in it, you don't need the subsequent setMap() call.
Also, you're missing a var for the i variable. I don't really recommend the coding style where all the var statements go at the top of a function. I find it error-prone; it's too easy to omit a var without noticing it. (I know some famous JavaScript experts insist that var at the top is the only way to do it, but they fail to see the tradeoffs involved.)
You don't need the .toString() on drawings[i]. It's already a string.
You have two different brace styles. Best to pick one and stick with it. For JavaScript, putting the { on a line by itself is not recommended, because this code will not do what you expect:
return // hoping to return an object literal - but it doesn't!
{
a: 'b',
c: 'd'
}
Whereas this code does work correctly:
return {
a: 'b',
c: 'd'
}
Since you are using FileReader, I think it's safe to assume you also have .forEach() available.
You can replace the code that uses .indexOf() and the hard coded length with a regular expression.
Putting all that together, you might end up with code like this:
var r = new FileReader();
r.onload = function( e ) {
e.target.result.split(";").forEach( function( drawing ) {
var match = drawing.match( /^(\w+)({.*})$/ );
if( ! match ) return; // unrecognized
var type = match[0], options = eval( match[1] );
switch( type ) {
case "Circle":
new google.maps.Circle( options );
break;
}
});
}
r.readAsText( f );
But you may be able to take it a step further. So far we're looking at a Circle (line breaks added for readability):
Circle{
center: new google.maps.LatLng(
38.041872419557094,
-87.6046371459961
),
radius:5197.017394363823,
fillColor: '#000000',
strokeWeight: 1,
strokeColor: '#000000',
map:map
}
With only a simple change, that could be executed as JavaScript directly. You just need the 'new google.maps.' at the beginning and () around the object literal:
new google.maps.Circle({
center: new google.maps.LatLng(
38.041872419557094,
-87.6046371459961
),
radius:5197.017394363823,
fillColor: '#000000',
strokeWeight: 1,
strokeColor: '#000000',
map:map
})
I assume you will have other drawing types as well? Will they all map directly to google.maps.* objects like Circle does? If so, you could simply do:
var r = new FileReader();
r.onload = function( e ) {
e.target.result.split(";").forEach( function( drawing ) {
eval( drawing.replace(
/^(\w+)({.*})$/,
'new google.maps.$1(\$2)'
) );
});
}
r.readAsText( f );
I'm making a simple function like this:
Game.msg = function(msg){
var m = Session.get("messages") || [];
m.push({"text": msg});
Session.set("messages", m);
};
and a template:
Template.field.messages = function(){
return Session.get("messages");
};
Triggering Game.msg() doesn't trigger an auto-update of the template. I suspect it's because the Array reference hasn't changed [even though the contents have]. What's the best way to trigger an update?
My hacky workaround is to have a dummy count variable (var c = Session.get("message_count")) which I set in Game.msg and reference in Template.field.messages, like this:
Game.msg = function(msg){
var m = Session.get("messages") || [];
m.push({"text": msg});
// silly, but adding a count so the array size changes and triggers a flush
Session.set("messages", m);
Session.set("message_count", m.length);
};
Template.field.messages = function(){
var c = Session.get("message_count");
return Session.get("messages");
};
How about using _.extend to create a new mutable object like this?
Game.msg = function(msg){
var m = Session.get("messages");
m = _.extend([], m);
m.push({"text": msg});
Session.set("messages", m);
};
I think it's a little bit clear than having a new variable in Session.
P.S. sorry, I have not enough reputation to comment, so I turn it into an answer.
My question is at the very end of the post.
I have tried everything from setting a timer for all the markers to be set to all kinds of calculations of the four corners, but nothing seems to be working.
Each time that I add a marker to the markermanager, I call this function below
public function markerSetBounds(someLat , someLng):void{
var bounds:LatLngBounds = new LatLngBounds();
for(var i:int = 0; i < myMarkers.length; i++)
{
var currentLatLon:LatLng = new LatLng(someLat , someLng);
bounds.extend(currentLatLon);
}
googleMap.setZoom(googleMap.getBoundsZoomLevel(bounds));
googleMap.setCenter(bounds.getCenter());
}
I believe I know why this does not work. I am only sending one set of lat, lng at a time.
However, when I tried the following, flex told me that it did not know what myMarkers[i].lat meant.
The following is how I fill myMarkers array
var someMarker:Marker = new Marker(new LatLng(someLat , someLng), new MarkerOptions({tooltip:someAddress, hasShadow: true}));
myMarkers.push(someMarker);
This is how I want to traverse through the array, but flex does not understand what .lat means.
for(var i:int = 0; i < myMarkers.length; i++)
{
var currentLatLon:LatLng = new LatLng(myMarkers[i].lat , myMarkers[i].lng);
bounds.extend(currentLatLon);
}
My question is how do I traverse through the myMarkers array to set currentLatLon. I have also tried a for each(var someObj:Marker in myMarkers) but it finds nothing. The markers are showing up on the map, but the bounds are not working.
Have you tried doing something like:
(myMarkers[i] as Marker).lat
Is this a problem at run time or compile time?
OK, I figured out was what the issue and it was that I had to place things in the correct order.
First, declare the LatLngBounds.
Second, make the markers.
Third, set the zoom
Forth, extend the bounds.
bounds = new LatLngBounds();
covToXML = new XML(event.result);
xmlToList = new XMLList(covToXML);
listToCol = new XMLListCollection(xmlToList);
someLat = Number(listToCol.children().child("geometry").child("location").child("lat").text());
someLng = Number(listToCol.children().child("geometry").child("location").child("lng").text());
someAddress = String(listToCol.children().child("formatted_address").text());
var markerOptions:MarkerOptions = new MarkerOptions();
markerOptions.icon = new (whichIcon(GlobalVars.randomIcon));
markerOptions.tooltip = someAddress;
markerOptions.hasShadow = true;
someMarker = new Marker(new LatLng(someLat , someLng), markerOptions);
someMarker.addEventListener(MapMouseEvent.CLICK,markerClicked);
myMarkers.push(someMarker);
googleMap.addOverlay(someMarker);
for each(someMarker in myMarkers)
{
var newLatLng:LatLng = someMarker.getLatLng();
// Alert.show(newLatLng.toString());
bounds.extend(newLatLng);
}
googleMap.setCenter(bounds.getCenter());
googleMap.setZoom(googleMap.getBoundsZoomLevel(bounds));
Thanks for all the suggestions and questions, which helped me to the solution.
Is it possible to sort an XMLList? All the examples I can find on it create a new XMLListCollection like this:
MyXMLListCol = new XMLListCollection(MyXMLList);
I don't think the XMLListCollection in this case has any reference to the XMLList so sorting it would leave my XMLList unsorted, is this correct?
How can I sort the XMLList directly?
Thanks
~Mike
So I finally got my search terms altered enough I actually churned up an answer to this.
Using the technique I got from here:
http://freerpad.blogspot.com/2007/07/more-hierarchical-sorting-e4x-xml-for.html
I was able to come up with this:
public function sortXMLListByAttribute(parentNode:XML,xList:XMLList,attr:String):void{
//attr values must be ints
var xListItems:int = xList.length();
if(xListItems !=0){
var sortingArray:Array = new Array();
var sortAttr:Number = new Number();
for each (var item:XML in xList){
sortAttr = Number(item.attribute(attr));
if(sortingArray.indexOf(sortAttr)==-1){
sortingArray.push(sortAttr);
}
//piggy back the removal, just have to remove all of one localName without touching items of other localNames
delete parentNode.child(item.localName())[0];
}
if( sortingArray.length > 1 ) {
sortingArray.sort(Array.NUMERIC);
}
var sortedList:XMLList = new XMLList();
for each(var sortedAttr:Number in sortingArray){
for each (var item2:XML in xList){
var tempVar:Number = Number(item2.attribute(attr));
if(tempVar == sortedAttr){
sortedList += item2
}
}
}
for each(var item3:XML in sortedList){
parentNode.appendChild(item3);
}
}
}
Works pretty fast and keeps my original XML variable updated. I know I may be reinventing the wheel just to not use an XMLListCollection, but I think the ability to sort XML and XMLLists can be pretty important
While there is no native equivalent to the Array.sortOn function, it is trivial enough to implement your own sorting algorithm:
// Bubble sort.
// always initialize variables -- it save memory.
var ordered:Boolean = false;
var l:int = xmlList.length();
var i:int = 0;
var curr:XML = null;
var plus:XML = null;
while( !ordered )
{
// Assume that the order is correct
ordered = true;
for( i = 0; i < l; i++ )
{
curr = xmlList[ i ];
plus = xmlList[ i + 1 ];
// If the order is incorrect, swap and set ordered to false.
if( Number( curr.#order ) < Number( plus.#order ) )
{
xmlList[ i ] = plus;
xmlList[ i + 1 ] = curr;
ordered = false;
}
}
}
but, realistically, it is far easier and less buggy to use XMLListCollection. Further, if someone else is reading your code, they will find it easier to understand. Please do yourself a favor and avoid re-inventing the wheel on this.