Here is my whole code to show a simple line:
public class MapUIViewController : UIViewController
{
MKMapView map;
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
CLLocationCoordinate2D loc = new CLLocationCoordinate2D(38.8895, -77.036168);
map = new MKMapView()
{
MapType = MKMapType.Hybrid,
Region = new MKCoordinateRegion(loc, new MKCoordinateSpan(0.1, 0.1)),
Frame = View.Bounds,
};
View = map;
ShowLine(loc, new CLLocationCoordinate2D(loc.Latitude + 1, loc.Longitude + 1));
}
void ShowLine(CLLocationCoordinate2D start, CLLocationCoordinate2D end)
{
MKGeodesicPolyline polyline = MKGeodesicPolyline.FromCoordinates(new CLLocationCoordinate2D[] { start, end });
map.AddOverlay(polyline);
map.OverlayRenderer = rend;
}
MKOverlayRenderer rend(MKMapView mapView, IMKOverlay overlay)
{
if (overlay is MKGeodesicPolyline line)
return new MKPolylineRenderer(line) { LineWidth = 3, StrokeColor = UIColor.Red, FillColor = UIColor.Brown, Alpha = 1 };
else
return null;
}
}
It shows the location specified, but does not show the line.
(The code is in C# (using Xamarin.ios) but it should be similar in Swift and Objective C, and my question is about the renderer which is an ios feature, not about a language feature).
I must be missing something, but I can't find it.
you need to assign the renderer BEFORE you add the line. Doing it the other way means that no renderer exists when the line is added so it will not be drawn
wrong
map.AddOverlay(polyline);
map.OverlayRenderer = rend;
right
map.OverlayRenderer = rend;
map.AddOverlay(polyline);
Related
I want to draw with one finger on a UIImageView-Control with Xamarin iOS.
What I found:
First
Second
This two examples won't work properly because with the first link there are the following problems:
I have to use two fingers for drawing, but I want to use only one Finger (donĀ“t be confused with the pictures, I have to use two fingers to active the touch function)
The positioning is wrong I guess the problems relies on due to wrong scaling (see the attached pictures)
This is the code I currently have:
public partial class DetailImageView : UIImageView
{
DetailImageViewController ctrl;
public DetailImageView (IntPtr handle) : base (handle)
{
}
public void Initialize(DetailImageViewController rootController)
{
ctrl = rootController;
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
UITouch touch = touches.AnyObject as UITouch;
drawOnTouch(touch);
//DrawLineOnImage();
}
public override void TouchesMoved(NSSet touches, UIEvent evt)
{
base.TouchesMoved(touches, evt);
UITouch touch = touches.AnyObject as UITouch;
drawOnTouch(touch);
//DrawLineOnImage();
}
private void drawOnTouch(object data)
{
UITouch touch = data as UITouch;
if (null != touch)
{
UIGraphics.BeginImageContext(this.Image == null ? this.Frame.Size : this.Image.Size);
//UIGraphics.BeginImageContext(this.Frame.Size);
using (CoreGraphics.CGContext cont = UIGraphics.GetCurrentContext())
{
if (this.Image != null)
{
cont.TranslateCTM(0f, this.Image.Size.Height);
cont.ScaleCTM(1.0f, -1.0f);
cont.DrawImage(new RectangleF(0f, 0f, Convert.ToSingle(this.Image.Size.Width)
, Convert.ToSingle(this.Image.Size.Height)), this.Image.CGImage);
cont.ScaleCTM(1.0f, -1.0f);
cont.TranslateCTM(0f, -this.Image.Size.Height);
} //end if
CGPoint lastLocation = touch.PreviousLocationInView(this);
CGPoint pt = touch.LocationInView(this);
using (CGPath path = new CGPath())
{
cont.SetLineCap(CGLineCap.Round);
cont.SetLineWidth(10);
cont.SetStrokeColor(0, 2, 3, 1);
var xMultiplier = Image.Size.Height / Frame.Height;
var yMultiplier = Image.Size.Width / Frame.Width;
var xLoc = lastLocation.X * xMultiplier;
var yLoc = lastLocation.Y * yMultiplier;
path.MoveToPoint(xLoc, yLoc);
path.AddLines(new CGPoint[] { new CGPoint(xLoc,
yLoc),
new CGPoint(pt.X * xMultiplier, pt.Y * yMultiplier) });
path.CloseSubpath();
cont.AddPath(path);
cont.DrawPath(CGPathDrawingMode.FillStroke);
this.Image = UIGraphics.GetImageFromCurrentImageContext();
}//end using path
}//end using cont
UIGraphics.EndImageContext();
this.SetNeedsDisplay();
}//end if
}//end void drawOnTouch
}
The positioning is wrong I guess the problems relies on due to wrong
scaling
Because when we use the image, the image's size is usually different from the UIImageView's size. You use the image's size to draw paths, so it will miss its position. Try to modify like this:
if (null != touch)
{
UIGraphics.BeginImageContext(this.Frame.Size);
using (CoreGraphics.CGContext cont = UIGraphics.GetCurrentContext())
{
if (this.Image != null)
{
cont.TranslateCTM(0f, this.Frame.Size.Height);
cont.ScaleCTM(1.0f, -1.0f);
cont.DrawImage(new RectangleF(0f, 0f, Convert.ToSingle(this.Frame.Size.Width)
, Convert.ToSingle(this.Frame.Size.Height)), this.Image.CGImage);
cont.ScaleCTM(1.0f, -1.0f);
cont.TranslateCTM(0f, -this.Frame.Size.Height);
} //end if
CGPoint lastLocation = touch.PreviousLocationInView(this);
CGPoint pt = touch.LocationInView(this);
using (CGPath path = new CGPath())
{
cont.SetLineCap(CGLineCap.Round);
cont.SetLineWidth(10);
cont.SetStrokeColor(0, 2, 3, 1);
var xLoc = lastLocation.X;
var yLoc = lastLocation.Y;
path.MoveToPoint(xLoc, yLoc);
path.AddLines(new CGPoint[] { new CGPoint(xLoc,
yLoc),
new CGPoint(pt.X, pt.Y) });
path.CloseSubpath();
cont.AddPath(path);
cont.DrawPath(CGPathDrawingMode.FillStroke);
this.Image = UIGraphics.GetImageFromCurrentImageContext();
}//end using path
}//end using cont
UIGraphics.EndImageContext();
this.SetNeedsDisplay();
}//end if
About
I have to use two fingers for drawing, but I want to use only one
Finger
What do you mean this? The code can be used drawing paths with only finger.
I am trying to draw a diagonal line in my Xamarin.iOS app using the graphics context in my UIView:
public override void Draw(CGRect rect)
{
base.Draw(rect);
using (var gctx = UIGraphics.GetCurrentContext())
{
var path = new CGPath();
path.AddLineToPoint(rect.Width, rect.Size.Height);
gctx.AddPath(path);
gctx.SetLineWidth(5);
gctx.SetFillColor(UIColor.Red.CGColor);
gctx.FillPath();
gctx.DrawPath(CGPathDrawingMode.FillStroke);
}
}
I expect to see a red diagonal line, but I don't see anything?
Solution:
You can achieve that via the code like the below code snippet:
public override void Draw(CGRect rect)
{
base.Draw(rect);
using (var gctx = UIGraphics.GetCurrentContext())
{
gctx.SetStrokeColor(UIColor.Red.CGColor);
gctx.SetLineWidth(2.0f);
gctx.MoveTo(0, 0);
gctx.AddLineToPoint(rect.Width, rect.Height);
gctx.StrokePath();
}
}
It works like this:
I am testing UNET (Unity 5.2) but is running into problem with things that probably should be very simple.
I have an Orange fruit (GameObject) that i can drag and have attached the network transform in code. When releasing the mouse (Mouse Up) I want to release the ownership of the Orange so none owns it, want later to attach to another player. I have tested with ReplacePlayerForConnection and a few other things but totally screwed up the code.
I have now reset everything and must ask for some help how to do this.
The scripts i have, attached to the Orange GameObject, is:
1
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class orange : NetworkBehaviour {
float distance = 10;
void Start() {
if (isLocalPlayer) {
//GameObject.Find("Main Camera").SetActive(false);
}
}
void OnMouseDrag() {
Vector3 mousePosition = new Vector3 (Input.mousePosition.x, Input.mousePosition.y, distance);
Vector3 objPosition = Camera.main.ScreenToWorldPoint (mousePosition);
transform.position = objPosition;
}
void OnMouseUp() {
print (">>> MOUSE UP <<<");
if (isLocalPlayer) {
GetComponent<NetworkIdentity> ().localPlayerAuthority = false;
GetComponent<NetworkIdentity> ().serverOnly = false;
}
}
Here is scrip #2 attached to the Orange GameObject:
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class Orange_SyncPosition : NetworkBehaviour {
[SyncVar] // Server will automatically transmit this value to all players when it changes
private Vector3 syncPos;
[SerializeField] Transform myTransform;
[SerializeField] float lerpRate = 15;
void FixedUpdate () {
TransmitPosition ();
LerpPosition ();
}
void LerpPosition() {
if (!isLocalPlayer) {
myTransform.position = Vector3.Lerp(myTransform.position, syncPos, Time.deltaTime *lerpRate);
}
}
[Command]
void CmdProvidePositionToServer (Vector3 pos) {
syncPos = pos;
}
[ClientCallback]
void TransmitPosition () {
if (isLocalPlayer) {
CmdProvidePositionToServer (myTransform.position);
}
}
}
Use NetworkServer.ReplacePlayerForConnection():
// Player is a NetworkBehaviour on the existing player object
void ReplacePlayer (Player existingPlayer) {
var conn = existingPlayer.connectionToClient;
var newPlayer = Instantiate<GameObject>(playerPrefab);
Destroy(existingPlayer.gameObject);
NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0);
}
Use AssignClientAuthority / RemoveClientAuthority
And never touch these parameters at runtime:
GetComponent<NetworkIdentity> ().localPlayerAuthority = false;
GetComponent<NetworkIdentity> ().serverOnly = false;
I am using mapField to create a custom map.I am using the code in this link.
How to show more than one location in Blackberry MapField?.
But the map position is fixed. i am not able to drag the map as we can do in google maps or when we invoke the maps like
public void execute(ReadOnlyCommandMetadata metadata, Object context)
{
Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, new MapsArguments());
}
Here's some code that should get you going on the correct path. I've taken it from a project of mine that had some special requirements, so there could be some remnants of that left in there inadvertently. There will be some undefined variables in there -- they're member variables that are declared in the class and should all start with an underscore. This is also part of a class that extends MapField, so you would have to create a custom map class and then use that rather than the default.
protected boolean touchEvent(TouchEvent message) {
boolean ret = super.touchEvent(message);
//mark that we're starting to interact
if(message.getEvent() == TouchEvent.DOWN) {
_startTouchTracking = true;
_clicking = true;
_touchX = message.getX(1);
_touchY = message.getY(1);
}
//user is wanting to move the map
else if(message.getEvent() == TouchEvent.MOVE) {
int dx = _touchX - message.getX(1);
int dy = _touchY - message.getY(1);
_clicking = false;
_touchX = message.getX(1);
_touchY = message.getY(1);
//perform checks to make sure we don't move outside of the map's range
int lat = getLatitude() - dy*(int)MathUtilities.pow(2, (double)getZoom());
if(lat < -9000000) {
lat = -9000000;
}
else if (lat > 9000000) {
lat = 9000000;
}
int lon = getLongitude() + dx*(int)MathUtilities.pow(2, (double)getZoom());
if(lon < -18000000) {
lon = -18000000;
}
else if (lon > 18000000) {
lon = 18000000;
}
moveTo(lat, lon);
}
//if the person just touches and releases, we want to move to that spot
else if (message.getEvent() == TouchEvent.UNCLICK && _clicking) {
int dx = message.getX(1) - getWidth()/2;
int dy = message.getY(1) - getHeight()/2;
move(dx, dy);
_clicking = false;
}
//touch has been released
else if (message.getEvent() == TouchEvent.UP) {
_startTouchTracking = false;
}
//we handled the click
return true;
}
As said, this might need tweaking for your use, but in general should get you started. The MathUtilities.pow() calls were my way of coming up with an appropriate amount of motion depending on the zoom level.
Edit for Comments
Letting a Bitmap move with the map:
protected Coordinates _bitmapCoordinates;
protected Bitmap _bitmap;
public YourMapField() {
//we're going to put the bitmap at -38.43, 20.32
_bitmapCoordinates = new Coordinates(-38.43, 20.32, 0.0);
_bitmap = YOUR_CODE_TO_GET_THE_BITMAP;
}
protected void paint(Graphics g) {
super.paint(g);
XYPoint placeToPaintBitmap = new XYPoint();
convertWorldToField(_bitmapCoordinates, placeToPaintBitmap);
//perform a check here to make sure that field will be seen. This code would depend
//on how you're painting the image. Just check the placeToPaintBitmap.x and placeToPaintBitmap.y
//against 0 and the map's width and height, along with some adjustment for how you paint
if(bitmap will be visible on the screen) {
//The code I have here is drawing the bitmap from the top left of the image, but if
//you need to draw from some other place you may have to offset the x and y
g.drawBitmap(placeToPaintBitmap.x, placeToPaintBitmap.y, _bitmap.getWidth(), _bitmap.getHeight(), 0, 0);
}
}
I didn't test any of that code, so it might be buggy but should give you the general idea.
Simple concept: I have a custom UIView which draws a path (series of lines). These lines are defined by the user via touches. After a touch, I want the Draw method to redraw the updated path/lines. Code:
Here is my custom UIView called "Renderer":
public partial class Renderer : MonoTouch.UIKit.UIView
{
enum eClickMode
{
BeginPoint,
ControlPoint,
EndPoint
}
eClickMode _ClickMode = eClickMode.BeginPoint;
CGPath _Path = new CGPath();
PointF _ControlPoint, _EndPoint;
UIImage _ImageRough;
UIColor _Rough;
public Renderer (IntPtr ptr)
{
_ImageRough = new UIImage("/Users/general/Desktop/deeprough.jpg");
_Rough = UIColor.FromPatternImage(_ImageRough);
UIColor.Black.SetStroke();
// Set initial path to make sure something gets drawn
_Path = new CGPath();
_Path.MoveToPoint(100, 100);
_Path.AddQuadCurveToPoint(200, 200, 100, 300);
}
public override void Draw (RectangleF rect)
{
base.Draw (rect);
CGContext gfx = UIGraphics.GetCurrentContext();
_Rough.SetFill();
gfx.SetLineWidth(0);
gfx.AddPath(_Path);
gfx.DrawPath(CGPathDrawingMode.FillStroke);
}
public override void TouchesEnded (NSSet touches, UIEvent evt)
{
base.TouchesEnded (touches, evt);
UITouch touch = touches.ToArray<UITouch>()[0];
PointF pt = touch.LocationInView(this);
switch (_ClickMode)
{
case Renderer.eClickMode.BeginPoint:
_Path.MoveToPoint(pt);
_ClickMode = Renderer.eClickMode.ControlPoint;
break;
case eClickMode.ControlPoint:
_ControlPoint = pt;
_ClickMode = Renderer.eClickMode.EndPoint;
break;
case eClickMode.EndPoint:
_EndPoint = pt;
_Path.AddQuadCurveToPoint(_ControlPoint.X, _ControlPoint.Y, _EndPoint.X, _EndPoint.Y);
_Path.MoveToPoint(_EndPoint);
this.SetNeedsDisplay();
break;
}
}
}
Here is my MainWindow.Designer code:
[MonoTouch.Foundation.Register("AppDelegate")]
public partial class AppDelegate {
private MonoTouch.UIKit.UIWindow __mt_window;
private Renderer __mt_renderer;
#pragma warning disable 0169
[MonoTouch.Foundation.Connect("window")]
private MonoTouch.UIKit.UIWindow window {
get {
this.__mt_window = ((MonoTouch.UIKit.UIWindow)(this.GetNativeField("window")));
return this.__mt_window;
}
set {
this.__mt_window = value;
this.SetNativeField("window", value);
}
}
[MonoTouch.Foundation.Connect("renderer")]
private Renderer renderer {
get {
this.__mt_renderer = ((Renderer)(this.GetNativeField("renderer")));
return this.__mt_renderer;
}
set {
this.__mt_renderer = value;
this.SetNativeField("renderer", value);
}
}
}
// Base type probably should be MonoTouch.UIKit.UIView or subclass
[MonoTouch.Foundation.Register("Renderer")]
public partial class Renderer {
}
I know for sure that in the TouchesEnded method, this.SetNeedsDisplay() is getting hit. But the Draw method only gets called initially.
I've seen other threads indicating a similar problem in Objective-C, but the solution seems to always be about threading. Well, I'm not threading (at least I'm not trying to anyway). This seems like a simple concept (and it is in .Net). Any help is GREATLY appreciated.
You aren't calling the correct base constructor on your UIView subclass, you have:
public Renderer (IntPtr ptr)
You want
public Renderer (IntPtr ptr) : base (ptr)