I have an app where the user can draw freely on a loaded image. He chooses a pencil color, and start drawing onto the image.
Along with that, he can scale the image down and up, using the fingers, that's where i'm having problems.
After scaling, when drawing onto the image, it gets blurry, and strangely shrinks..
Code for scaling:
void ScaleImage (UIPinchGestureRecognizer gestureRecognizer)
{
AdjustAnchorPoint (gestureRecognizer);
if (gestureRecognizer.State == UIGestureRecognizerState.Began || gestureRecognizer.State == UIGestureRecognizerState.Changed) {
gestureRecognizer.View.Transform *= CGAffineTransform.MakeScale (gestureRecognizer.Scale, gestureRecognizer.Scale);
gestureRecognizer.Scale = 1;
}
}
Code for drawing:
public override void TouchesBegan (NSSet touches, UIEvent evt)
{
base.TouchesBegan (touches, evt);
if (!canDraw)
return;
UITouch touch = (UITouch)touches.AnyObject;
lastPoint = touch.LocationInView (MyImageView);
}
public override void TouchesMoved (NSSet touches, UIEvent evt)
{
base.TouchesMoved (touches, evt);
if (!canDraw)
return;
UITouch touch = (UITouch)touches.AnyObject;
PointF currentPoint = touch.LocationInView (MyImageView);
UIGraphics.BeginImageContext (MyImageView.Frame.Size);
MyImageView.Image.Draw (new RectangleF (
0, 0,
MyImageView.Frame.Size.Width,
MyImageView.Frame.Size.Height));
CGContext ctx = UIGraphics.GetCurrentContext ();
ctx.SetLineCap (CGLineCap.Round);
ctx.SetLineWidth (5.0f);
ctx.SetRGBStrokeColor (red, green, blue, 1);
CGPath path = new CGPath ();
path.MoveToPoint(MyImageView.Transform, lastPoint.X, lastPoint.Y);
path.AddLineToPoint (MyImageView.Transform, currentPoint.X, currentPoint.Y);
ctx.AddPath (path);
ctx.DrawPath (CGPathDrawingMode.Stroke);
MyImageView.Image = UIGraphics.GetImageFromCurrentImageContext ();
UIGraphics.EndImageContext ();
lastPoint = currentPoint;
}
Thanks in advance!
I made it work by using Bezier Paths instead of Core Graphics.
You can find some background about Bezier Paths here:
https://developer.apple.com/library/ios/documentation/2ddrawing/conceptual/drawingprintingios/BezierPaths/BezierPaths.html
Since i wanted to implement an Undo button, i created a Class for storing the Color of the line, as well as the corresponding Path:
public class VESLine{
public UIBezierPath Path {
get;
set;
}
public UIColor Color {
get;
set;
}
public byte Index {
get;
set;
}
}
The TouchesBegan event was modified accordingly, to create a new UIBezierPath object, store it in the array, along with its Color and Index, and touch location:
public override void TouchesBegan (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
IndexCount++;
UIBezierPath path = new UIBezierPath ();
path.LineWidth = 5.0f;
UITouch touch = (UITouch)touches.AnyObject;
previousPoint1 = touch.PreviousLocationInView (this);
PointF p = touch.LocationInView (this);
path.MoveTo (p);
InvokeOnMainThread (() => {
this.SetNeedsDisplay ();
});
currentPath = path;
VESLine line = new VESLine () {
Path = currentPath,
Color = StrokeColor,
Index = IndexCount
};
lines.Add(line);
}
TouchesMoved event was also modified, to create a smooth line path, by using a QuadCurve instead of a regular line. I also call the SetNeedsDisplay method, to force view drawing:
public override void TouchesMoved (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
UITouch touch = (UITouch)touches.AnyObject;
PointF p = touch.LocationInView (this);
if (Math.Abs (p.X - previousPoint1.X) >= 4 ||
Math.Abs (p.Y - previousPoint1.Y) >= 4) {
PointF cP = new PointF ((p.X + previousPoint1.X) / 2, (p.Y + previousPoint1.Y) / 2);
currentPath.AddQuadCurveToPoint (cP, previousPoint1);
previousPoint1 = p;
}
else
currentPath.AddLineTo (p);
InvokeOnMainThread (() => {
this.SetNeedsDisplay ();
});
}
TouchesEnded and TouchesCancelled will redraw the view:
public override void TouchesEnded (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
InvokeOnMainThread (() => {
this.SetNeedsDisplay ();
});
}
public override void TouchesCancelled (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
InvokeOnMainThread (() => {
this.SetNeedsDisplay ();
});
}
Finally, the Draw method was rewritten to iterate the Array of paths, drawing each one of them using the corresponding color:
public override void Draw (System.Drawing.RectangleF rect)
{
foreach (VESLine p in lines) {
p.Color.SetStroke ();
p.Path.Stroke ();
}
}
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 use Mouse.GetState() for my menu selection. Currently, it will only highlight if I hover over a region left and up from where the menu is. I used DrawString to display the mouses coordinates and found that the 0,0 point wasn't in the top left of my monitor or in the top left of the game window. It was somewhere about 100,100 pixels from the top left of the screen. Also, the 0,0 point moves every time I run the programme.
I looked at others people who have had the same problem but wasn't able to solve it. I tried using Mouse.WindowHandle = this.Window.Handle; in my Initialize() but it didn't nothing. I have two monitors and when I forced the game in fullscreen it would open on my second monitor so I disabled it but the problem remains.
here is a link to my code http://pastebin.com/PNaFADqp
Game1 class:
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont spriteFont;
public const int WINDOW_HEIGHT = 800;
public const int WINDOW_WIDTH = 600;
public int tree;
public TitleScreen titleScreen;
public SATDemo satDemo;
public SeparatingAxisTest separatingAxisTest;
public SATWithAABB sATWithAABB;
GameState currentState;
public static Dictionary<string, Texture2D> m_textureLibrary = new Dictionary<string, Texture2D>();
public static Dictionary<string, SpriteFont> m_fontLibrary = new Dictionary<string, SpriteFont>();
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferHeight = WINDOW_HEIGHT;
graphics.PreferredBackBufferWidth = WINDOW_WIDTH;
}
protected override void Initialize()
{
Mouse.WindowHandle = this.Window.Handle;
//enable the mousepointer
IsMouseVisible = true;
currentState = GameState.TitleScreen;
//sets the windows mouse handle to client bounds handle
base.Initialize();
}
public void RequestSATDemo()
{
currentState = GameState.RequestSATDemo;
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
m_textureLibrary.Add("Pixel", Content.Load<Texture2D>("White_Pixel"));
m_fontLibrary.Add("Font", Content.Load<SpriteFont>("MotorwerkOblique"));
titleScreen = new TitleScreen();
satDemo = new SATDemo();
separatingAxisTest = new SeparatingAxisTest();
sATWithAABB = new SATWithAABB();
}
public void RequestSeparatingAxisTest()
{
currentState = GameState.SeparatingAxisTest;
}
public void RequestSATWithAABB()
{
currentState = GameState.SATWithAABB;
}
protected override void Update(GameTime gameTime)
{
MouseTestState = Mouse.GetState();
switch (currentState)
{
case GameState.TitleScreen:
{
titleScreen.Update(gameTime);
break;
}
case GameState.SeparatingAxisTest:
{
separatingAxisTest.Update(gameTime);
break;
}
case GameState.SATWithAABB:
{
sATWithAABB.Update(gameTime);
break;
}
case GameState.Exit:
{
Exit();
break;
}
default:
{
titleScreen.Update(gameTime);
break;
}
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
spriteBatch.DrawString(m_fontLibrary["Font"], MouseTestState.ToString(), new Vector2(0, 0), Color.White);
switch (currentState)
{
case GameState.TitleScreen:
{
titleScreen.Draw(spriteBatch, spriteFont);
break;
}
case GameState.SeparatingAxisTest:
{
separatingAxisTest.Draw(gameTime, spriteBatch);
break;
}
case GameState.SATWithAABB:
{
sATWithAABB.Draw(gameTime, spriteBatch);
break;
}
case GameState.Exit:
{
Exit();
break;
}
default:
{
titleScreen.Update(gameTime);
break;
}
}
spriteBatch.End();
base.Draw(gameTime);
}
}
TitleScreen class:
public class TitleScreen : Screen
{
List<Button> buttonList = new List<Button>();
public Menu mainMenu;
public TitleScreen()
{
mainMenu = new Menu(new Vector2(200, 100), buttonList, 0);
buttonList.Add(new PushButton("Separating Axis Test"));
buttonList.Add(new PushButton("SAT With AABB"));
buttonList.Add(new PushButton("Awesome"));
buttonList.Add(new PushButton("Awesomere"));
buttonList.Add(new PushButton("Awesomere"));
}
public override void Update(GameTime gametime)
{
mainMenu.Update(gametime);
}
public void Draw(SpriteBatch sB, SpriteFont sF)
{
mainMenu.Draw(sB, sF);
}
}
PushButton class:
public class PushButton : Button
{
string m_text;
SpriteFont m_font;
Color m_static, m_onClick, m_onHover;
Texture2D m_sprite2D, m_onClick2D;
static public int Pbuttoncount;
//click processing
bool m_clickedInside = false,
m_releasedInside = false,
m_OnClicked = false,
selected = false;
Rectangle drawRectangle;
public PushButton(string Text)
{
m_text = Text;
drawRectangle = new Rectangle((int)Menu.m_position.X, (int)Menu.m_position.Y + (15 * Pbuttoncount), 200, 15);
ButtonRegion = new Rectangle((int)Position.X, (int)Position.Y, 200, 15);
Pbuttoncount++;
}
public PushButton(Rectangle ButtonRegion, SpriteFont Font, string Text, Color Static, Color OnClick, Color OnHover)
{
m_buttonRegion = ButtonRegion;
m_font = Font;
m_text = Text;
m_static = Static;
m_onClick = OnClick;
m_onHover = OnHover;
// drawRectangle = ButtonPosition(m_buttonRegion);
}
public PushButton(Rectangle ButtonRegion, Texture2D Sprite2D, Texture2D OnClick2D)
{
m_buttonRegion = ButtonRegion;
m_sprite2D = Sprite2D;
m_onClick2D = OnClick2D;
//drawRectangle = ButtonPosition(m_buttonRegion);
}
public override void Update(GameTime gameTime)
{
MouseState currentMouse = Mouse.GetState();
selected = MouseState(drawRectangle, currentMouse);
m_clickedInside = ClickInside(currentMouse, m_lastMouseState);
ReleaseInside(currentMouse, m_lastMouseState);
if (selected && m_clickedInside && m_releasedInside)
m_OnClicked = true;
else
m_OnClicked = false;
m_lastMouseState = currentMouse;
}
public override void Draw(SpriteBatch spriteBatch, SpriteFont spriteFont, int buttonCount, Vector2 Position)
{
spriteBatch.Draw(Game1.m_textureLibrary["Pixel"], new Rectangle((int)Position.X + 10, (int)(Position.Y + 15 * buttonCount), 180, 15), Color.Wheat);
if (selected)
spriteBatch.DrawString(Game1.m_fontLibrary["Font"], m_text, new Vector2(Position.X + 15, Position.Y + 15 * buttonCount), Color.Orange);
else
spriteBatch.DrawString(Game1.m_fontLibrary["Font"], m_text, new Vector2(Position.X + 15, Position.Y + 15 * buttonCount), Color.Black);
}
}
Menu class:
public class Menu
{
List<Button> m_buttonList;
float m_transparency;
public int n = 0;
public Rectangle buttonRegion, m_menuRegion, m_dimensions;
static public Vector2 m_position;
int m_WINDOW_HEIGHT = Game1.WINDOW_HEIGHT;
int m_WINDOW_WIDTH = Game1.WINDOW_WIDTH;
private Game1 m_managerClass;
public Menu(Vector2 Position, List<Button> ButtonList, float Transparency)
{
m_position = Position;
m_buttonList = ButtonList;
m_transparency = Transparency;
m_managerClass = new Game1();
}
public Rectangle MenuRegion
{
get { return m_menuRegion; }
set { m_menuRegion = value; }
}
static public Vector2 Position
{
get { return m_position; }
}
public void Update(GameTime gametime)
{
for (int i = 0; i < m_buttonList.Count; i++)
{
m_buttonList[i].Update(gametime);
if (m_buttonList[0].OnClicked)
{
SeperatingAxisTest();
}
}
}
public void Draw(SpriteBatch sB, SpriteFont sF)
{
sB.Draw(Game1.m_textureLibrary["Pixel"], new Rectangle((int)m_position.X - 5, (int)m_position.Y - 10, (m_buttonList[0].ButtonRegion.Width + 10), (m_buttonList[0].ButtonRegion.Height * m_buttonList.Count) + 20), Color.Blue);
for (int i = 0; i < m_buttonList.Count; i++)
{
m_buttonList[i].Draw(sB, sF, i, new Vector2(Position.X, Position.Y));
}
}
private void SeperatingAxisTest()
{
m_managerClass.RequestSeparatingAxisTest();
}
}
Program class:
public static class Program
{
[STAThread]
static void Main()
{
using (var game = new Game1())
game.Run();
}
}
Let me know if you need anything else. I'm still learning and will sell my soul to you for an answer.
Your Menu class is creating a new instance of Game1. This is, most likely, not what you want, since Game1 is instantiated in the entry point for you app. The Game1 instance has an instance of TitleScreen, which in turn has an instance of the Menu class, so a Menu should have no business creating its own game.
When this (other) instance is created, it invokes platform-specific (Windows) methods, creates an additional window handle (which is never shown) and configures the Mouse.WindowHandle.
And btw, setting WindowHandle manually does absolutely nothing in Monogame, so all these sources mentioning that are talking about XNA.
So, there are several remarks:
You should probably have a "screen manager" class which contains the current screen. It is strange to have a field of type TitleScreen in your game class, it should at least be of the base type (Screen), so that the game class draws and updates each screen transparently.
If you need a reference to the game class anywhere, don't instantiate a new one, but rather pass it along through the constructor.
m_managerClass is a bad name for a field which is actually a Game. Also google for C# naming conventions. Perhaps you even might want to download an existing monogame game template, e.g. check some of the samples online; the NetRumble sample seems to implement a screen manager.
Remove the Mouse.WindowHandle line, it should be set to your one-and-only game window by default.
tl;dr add the Game1 as a parameter wherever you might need it (but only where you need it).
abstract class Screen
{
private readonly Game1 _game;
public Game1 Game
{ get { return _game; } }
public Screen(Game1 game)
{
_game = game;
}
}
class TitleScreen : Screen
{
public TitleScreen(Game1 game)
: base(game)
{ ... }
}
class Menu
{
private readonly Screen _screen;
public Menu(Screen parentScreen, Vector2 pos, List<Button> list, float alpha)
{
_screen = parentScreen;
...
// if you need the game instance, just use _screen.Game
}
}
Hi, I'm working on a 2D game and I was working on the scrolling background but whatever I try it doesn't get it scrolled.
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//The size of the Sprite
public Rectangle Size;
//Used to size the Sprite up or down from the original image
public float Scale = 1.0f;
// Create an instance of Texture2D that will
// contain the background texture.
Texture2D background;
// Create a Rectangle that will definee
// the limits for the main game screen.
Rectangle mainFrame;
private GamePadState gamePadState;
private KeyboardState keyboardState;
public class Camera
{
public Camera(Viewport viewport)
{
Origin = new Vector2(viewport.Width / 2.0f, viewport.Height / 2.0f);
Zoom = 1.0f;
}
public Vector2 Position { get; set; }
public Vector2 Origin { get; set; }
public float Zoom { get; set; }
public float Rotation { get; set; }
public Matrix GetViewMatrix(Vector2 parallax)
{
// To add parallax, simply multiply it by the position
return Matrix.CreateTranslation(new Vector3(-Position * parallax, 0.0f)) *
// The next line has a catch. See note below.
Matrix.CreateTranslation(new Vector3(-Origin, 0.0f)) *
Matrix.CreateRotationZ(Rotation) *
Matrix.CreateScale(Zoom, Zoom, 1) *
Matrix.CreateTranslation(new Vector3(Origin, 0.0f));
}
}
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
graphics.IsFullScreen = true;
}
protected override void Initialize()
{
gamePadState = GamePad.GetState(PlayerIndex.One);
keyboardState = Keyboard.GetState();
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Load the background content.
background = Content.Load<Texture2D>("Images\\muur");
// Set the rectangle parameters.
mainFrame = new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
// Draw the background.
// Start building the sprite.
spriteBatch.Begin();
// Draw the background.
spriteBatch.Draw(background, mainFrame, Color.White);
// End building the sprite.
spriteBatch.End();
base.Draw(gameTime);
}
}
How can I achieve this functionality?
I don't see where you use the camera transform...
you should have a parallax vector and update it...
Vector2 parallax_position;
float parallax_speed;
public void Update (Gametime time)
{
parallax_position += parallax_speed * Vector2.UnitX * (float) time.elapsed.totalseconds;
}
and then in Draw method, you should use it in your spritebatch....
public void Draw()
{
spriteBatch.begin(..,...,..,..,.., GetCameraTransform(Parallax));
...
}
I was wondering if MonoTouch allows the developer to change the colour of UIPageControl dots to suit a light background - they seem to be fixed white, which makes them very hard to see on a light textured background.
I am aware there is no public API available for this but I was wondering if anything was internally implemented in MonoTouch to improve on this.
Otherwise, what's the recommended approach to using a UIPageControl on a light background?
I took a stab at translating this. I'm not sure it will work, but it does compile. Note that the page linked to contains comments indicating that Apple frowns upon this code and may reject it:
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace Whatever
{
public class StyledPageControl : UIPageControl
{
public StyledPageControl () : base()
{
}
public override int CurrentPage {
get {
return base.CurrentPage;
}
set {
base.CurrentPage = value;
string imgActive = NSBundle.MainBundle.PathForResource("activeImage", "png");
string imgInactive = NSBundle.MainBundle.PathForResource("inactiveImage", "png");
for (int subviewIndex = 0; subviewIndex < this.Subviews.Length; subviewIndex++)
{
UIImageView subview = this.Subviews[subviewIndex] as UIImageView;
if (subviewIndex == value)
subview.Image = UIImage.FromFile(imgActive);
else
subview.Image = UIImage.FromFile(imgInactive);
}
}
}
public override int Pages {
get {
return base.Pages;
}
set {
base.Pages = value;
string img = NSBundle.MainBundle.PathForResource("inactiveImage", "png");
for (int subviewIndex = 0; subviewIndex < this.Subviews.Length; subviewIndex++)
{
UIImageView subview = this.Subviews[subviewIndex] as UIImageView;
subview.Image = UIImage.FromFile(img);
}
}
}
}
}
Convert the following code to C#: http://apptech.next-munich.com/2010/04/customizing-uipagecontrols-looks.html
I combined this and this for monotouch. I hope this helps.
The usage is like this:
_pager.Change += delegate(object sender, EventArgs e) {
var pc = sender as PageControl;
Console.WriteLine ("Change Delegate== " + pc.currentPage);
var toPage = pc.currentPage;
var pageOffset = _scroll.Frame.Width*toPage;
PointF p = new PointF(pageOffset, 0);
Console.WriteLine (pageOffset);
_scroll.SetContentOffset(p,true);
};
And the class like this.
public class PageControl:UIView
{
#region ctor
public PageControl (RectangleF rect) :base(rect)
{
this.BackgroundColor = UIColor.Red;
this.CurrenColor=new CGColor(.2f,15f,10F);
this.OtherColor =new CGColor(.77F,.71F,.60F);
}
#endregion
#region Fields
float kDotDiameter= 7.0f;
float kDotSpacer = 7.0f;
int _currentPage;
int _numberOfPages;
CGColor CurrenColor{get;set;}
CGColor OtherColor{get;set;}
public int currentPage
{
set
{
_currentPage = Math.Min(Math.Max(0, value),_numberOfPages-1);
this.SetNeedsDisplay();
}
get{return _currentPage;}
}
public int numberOfPages
{
set
{
_numberOfPages = Math.Max(0,value);
_currentPage = Math.Min(Math.Max(0, _currentPage), _numberOfPages-1);
this.SetNeedsDisplay();
}
get{return _numberOfPages;}
}
#endregion
#region Overrides
public override void Draw (RectangleF rect)
{
base.Draw (rect);
CGContext context = UIGraphics.GetCurrentContext();
context.SetAllowsAntialiasing(true);
RectangleF currentBounds = this.Bounds;
float dotsWidth = this.numberOfPages*kDotDiameter + Math.Max(0,this.numberOfPages-1)*kDotSpacer;
float x = currentBounds.GetMidX() - dotsWidth/2;
float y = currentBounds.GetMidY() - kDotDiameter/2;
for (int i = 0; i < _numberOfPages; i++) {
RectangleF circleRect = new RectangleF(x,y,kDotDiameter,kDotDiameter);
if (i==_currentPage) {
context.SetFillColor(this.CurrenColor);
}
else {
context.SetFillColor(this.OtherColor);
}
context.FillEllipseInRect(circleRect);
x += kDotDiameter + kDotSpacer;
}
}
public override void TouchesBegan (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
base.TouchesBegan (touches, evt);
PointF touchpoint = (touches.AnyObject as MonoTouch.UIKit.UITouch).LocationInView(this);
RectangleF currentbounds = this.Bounds;
float x = touchpoint.X- currentbounds.GetMidX();
if (x<0 && this.currentPage>=0) {
this.currentPage--;
Change(this,EventArgs.Empty);
}
else if (x>0 && this.currentPage<this.numberOfPages-1) {
this.currentPage++;
Change(this,EventArgs.Empty);
}
}
#endregion
#region delegate
public event EventHandler Change;
#endregion
}
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)