event.getAction() misses out on MotionEvent.ACTION_MOVE - android-view

I have an app that draws a grid of dots (let's say 3x3). The user is asked to draw something on that grid. If the user's finger touches one of the dots in the grid, this dot is being colored. In addition a line will be drawn between each two touched dots.
The issue - event.getAction() will often miss out on MotionEvent.ACTION_MOVE. What do I mean? - well, let's say the user drew a straight line connecting three dots. Often the first dot will be colored, the third (last) dot will be colored but the second (middle) dot will not get colored.
So I logged what else if(event.getAction() == MotionEvent.ACTION_MOVE) is doing and found out that often event.getAction() will not "notice" the change in finger location on the screen.
I noticed that if I pass my finger SLOWER along the imaginary path I want to draw, event.getAction() better notices the finger movements.
After some testing I've also noticed that as I draw more and more lines on the screen, the game becomes increasingly slower/sluggish. It seems like that app is collecting so much data (event data?) that it doesn't manage to process it in a timely manner (I get the log: I/Choreographer: Skipped 45 frames! The application may be doing too much work on its main thread). Could this be an issue of an over-sized cache of some sort? How can I clean that "cache" once I know the user's action is done?
Does anyone have experience with such a case where the system will not track finger movements fast enough?
============ FURTHER EXPERIMENTATION ==========================
After reading what #Jimpanzee wrote I delved more into the documentation: I read about using getHistoricalX(int, int) and getHistoricalY(int, int). As it seems Android batches motion events so getX & getY might deliver only the LAST event in that batch. This can be a problem if you have fast movements on the screen. So I turned to use getHistoricalX and getHistoricalY in addition to getX and getY. This for some strange reason didn't solve the problem: I still get dots that are being touched but not getting colored.
An example can be seen below:
I am using an ArrayList>
Each ArrayList defines a path made out of dots by the order they were touched.
an ArrayList holds all thos paths
The grid is numbered 0, 100, 200, 300, 400 on each the X and Y axis
The first dot to be fainted was [100, 100] - the finger went all the way down, painting four dots, last dot being [100, 400].
the second line was also drawn from top to bottom but you can see that the third dot from the top [200, 300] is missing.
I couldn't explain that so I logged the touch events:
moveH means action MOVE + historical data
moveC means action MOVE + current data
When a touched point was close to a dot on the grid [add point] is noted
From time to time the whole array of paths is printed out
If you look at the log, you will see that after adding point [200, 200] the Y values get strange - they jump from 212.53839 (in the last seen historical point) to 412.45544 and stays high all the way until the next dot is added to the array. That explains why dot [200, 300] was left out and never drawn.
383: Action moveH x: 198.75 y: 284.8833
383: Action moveH x: 199.6875 y: 305.07257
387: Action moveH x: 204.84375 y: 326.94427
387: Action moveH x: 196.875 y: 212.53839
387: Action moveH [add point] x: 200 y: 200
387: [[Point(100, 100), Point(100, 200), Point(100, 300), Point(100, 400)], [Point(200, 100), Point(200, 200)]]
504: Action moveE x: 210.0 y: 412.45544
504: Action moveH x: 205.78125 y: 345.4511
504: Action moveH x: 207.1875 y: 362.2755
504: Action moveH x: 207.65625 y: 369.00528
504: Action moveH x: 208.125 y: 378.2587
504: Action moveH x: 209.53125 y: 397.60675
504: Action moveH x: 209.53125 y: 400.97162
508: Action moveH x: 210.0 y: 404.3365
508: Action moveH x: 210.0 y: 407.7014
508: Action moveH x: 210.0 y: 411.90747
508: Action moveH x: 205.78125 y: 345.4511
508: Action moveH x: 207.1875 y: 362.2755
508: Action moveH x: 207.65625 y: 369.00528
508: Action moveH x: 208.125 y: 378.2587
508: Action moveH x: 209.53125 y: 397.60675
508: Action moveH x: 209.53125 y: 400.97162
508: Action moveH x: 210.0 y: 404.3365
508: Action moveH x: 210.0 y: 407.7014
508: Action moveH x: 210.0 y: 411.90747
508: Action moveH x: 205.78125 y: 345.4511
508: Action moveH x: 207.1875 y: 362.2755
508: Action moveH x: 207.65625 y: 369.00528
508: Action moveH x: 208.125 y: 378.2587
508: Action moveH x: 209.53125 y: 397.60675
508: Action moveH x: 209.53125 y: 400.97162
512: Action moveH x: 210.0 y: 404.3365
512: Action moveH x: 210.0 y: 407.7014
512: Action moveH x: 210.0 y: 411.90747
512: Action moveH x: 205.78125 y: 345.4511
512: Action moveH x: 207.1875 y: 362.2755
512: Action moveH x: 207.65625 y: 369.00528
512: Action moveH x: 208.125 y: 378.2587
512: Action moveH x: 209.53125 y: 397.60675
512: Action moveH x: 209.53125 y: 400.97162
512: Action moveH x: 210.0 y: 404.3365
512: Action moveH x: 210.0 y: 407.7014
512: Action moveH x: 210.0 y: 411.90747
512: Action moveH x: 205.78125 y: 345.4511
515: Action moveH x: 207.1875 y: 362.2755
515: Action moveH x: 207.65625 y: 369.00528
515: Action moveH x: 208.125 y: 378.2587
515: Action moveH x: 209.53125 y: 397.60675
515: Action moveH x: 209.53125 y: 400.97162
515: Action moveH x: 210.0 y: 404.3365
515: Action moveH x: 210.0 y: 407.7014
515: Action moveH x: 210.0 y: 411.90747
515: Action moveH x: 205.78125 y: 345.4511
515: Action moveH x: 207.1875 y: 362.2755
515: Action moveH x: 207.65625 y: 369.00528
515: Action moveH x: 208.125 y: 378.2587
515: Action moveH x: 209.53125 y: 397.60675
515: Action moveH x: 209.53125 y: 400.97162
515: Action moveH x: 210.0 y: 404.3365
515: Action moveH x: 210.0 y: 407.7014
515: Action moveH x: 210.0 y: 411.90747
519: Action moveH x: 205.78125 y: 345.4511
519: Action moveH x: 207.1875 y: 362.2755
519: Action moveH x: 207.65625 y: 369.00528
519: Action moveH x: 208.125 y: 378.2587
519: Action moveH x: 209.53125 y: 397.60675
519: Action moveH x: 209.53125 y: 400.97162
519: Action moveH x: 210.0 y: 404.3365
519: Action moveH x: 210.0 y: 407.7014
519: Action moveH x: 210.0 y: 411.90747
523: Action moveH x: 205.78125 y: 345.4511
523: Action moveH x: 207.1875 y: 362.2755
523: Action moveH x: 207.65625 y: 369.00528
527: Action moveH x: 208.125 y: 378.2587
527: Action moveH x: 209.53125 y: 397.60675
527: Action moveH x: 209.53125 y: 400.97162
527: Action moveH x: 210.0 y: 404.3365
527: Action moveH x: 210.0 y: 407.7014
527: Action moveH x: 210.0 y: 411.90747
527: Action moveH x: 205.78125 y: 345.4511
527: Action moveH x: 207.1875 y: 362.2755
527: Action moveH x: 207.65625 y: 369.00528
527: Action moveH x: 208.125 y: 378.2587
527: Action moveH x: 209.53125 y: 397.60675
527: Action moveH x: 209.53125 y: 400.97162
527: Action moveH x: 210.0 y: 404.3365
527: Action moveH x: 210.0 y: 407.7014
527: Action moveH x: 210.0 y: 411.90747
531: Action moveH x: 205.78125 y: 345.4511
531: Action moveH x: 207.1875 y: 362.2755
531: Action moveH x: 207.65625 y: 369.00528
531: Action moveH x: 208.125 y: 378.2587
531: Action moveH x: 209.53125 y: 397.60675
531: Action moveH x: 209.53125 y: 400.97162
531: Action moveH x: 210.0 y: 404.3365
531: Action moveH x: 210.0 y: 407.7014
531: Action moveH x: 210.0 y: 411.90747
531: Action moveH x: 205.78125 y: 345.4511
531: Action moveH x: 207.1875 y: 362.2755
531: Action moveH x: 207.65625 y: 369.00528
531: Action moveH x: 208.125 y: 378.2587
531: Action moveH x: 209.53125 y: 397.60675
531: Action moveH x: 209.53125 y: 400.97162
531: Action moveH x: 210.0 y: 404.3365
531: Action moveH x: 210.0 y: 407.7014
535: Action moveH x: 210.0 y: 411.90747
535: Action moveH x: 205.78125 y: 345.4511
535: Action moveH x: 207.1875 y: 362.2755
535: Action moveH x: 207.65625 y: 369.00528
535: Action moveH x: 208.125 y: 378.2587
535: Action moveH x: 209.53125 y: 397.60675
535: Action moveH x: 209.53125 y: 400.97162
535: Action moveH x: 210.0 y: 404.3365
535: Action moveH x: 210.0 y: 407.7014
535: Action moveH x: 210.0 y: 411.90747
535: Action moveH x: 205.78125 y: 345.4511
535: Action moveH x: 207.1875 y: 362.2755
535: Action moveH x: 207.65625 y: 369.00528
535: Action moveH x: 208.125 y: 378.2587
535: Action moveH x: 209.53125 y: 397.60675
535: Action moveH x: 209.53125 y: 400.97162
535: Action moveH x: 210.0 y: 404.3365
535: Action moveH x: 210.0 y: 407.7014
535: Action moveH x: 210.0 y: 411.90747
535: Action moveH x: 205.78125 y: 345.4511
539: Action moveH x: 207.1875 y: 362.2755
539: Action moveH x: 207.65625 y: 369.00528
539: Action moveH x: 208.125 y: 378.2587
539: Action moveH x: 209.53125 y: 397.60675
539: Action moveH x: 209.53125 y: 400.97162
539: Action moveH x: 210.0 y: 404.3365
539: Action moveH x: 210.0 y: 407.7014
539: Action moveH x: 210.0 y: 411.90747
539: Action moveH x: 205.78125 y: 345.4511
539: Action moveH x: 207.1875 y: 362.2755
539: Action moveH x: 207.65625 y: 369.00528
539: Action moveH x: 208.125 y: 378.2587
539: Action moveH x: 209.53125 y: 397.60675
539: Action moveH x: 209.53125 y: 400.97162
539: Action moveH x: 210.0 y: 404.3365
539: Action moveH x: 210.0 y: 407.7014
539: Action moveH x: 210.0 y: 411.90747
539: Action moveH x: 205.78125 y: 345.4511
539: Action moveH x: 207.1875 y: 362.2755
539: Action moveH x: 207.65625 y: 369.00528
543: Action moveH x: 208.125 y: 378.2587
543: Action moveH x: 209.53125 y: 397.60675
543: Action moveH x: 209.53125 y: 400.97162
543: Action moveH x: 210.0 y: 404.3365
543: Action moveH x: 210.0 y: 407.7014
543: Action moveH x: 210.0 y: 411.90747
543: Action moveH x: 205.78125 y: 345.4511
543: Action moveH x: 207.1875 y: 362.2755
543: Action moveH x: 207.65625 y: 369.00528
543: Action moveH x: 208.125 y: 378.2587
543: Action moveH x: 209.53125 y: 397.60675
543: Action moveH x: 209.53125 y: 400.97162
543: Action moveH x: 210.0 y: 404.3365
543: Action moveH x: 210.0 y: 407.7014
543: Action moveH x: 210.0 y: 411.90747
543: Action moveH x: 205.78125 y: 345.4511
543: Action moveH x: 207.1875 y: 362.2755
543: Action moveH x: 207.65625 y: 369.00528
543: Action moveH x: 208.125 y: 378.2587
543: Action moveH x: 209.53125 y: 397.60675
543: Action moveH x: 209.53125 y: 400.97162
543: Action moveH x: 210.0 y: 404.3365
543: Action moveH x: 210.0 y: 407.7014
543: Action moveH x: 210.0 y: 411.90747
547: Action moveH x: 205.78125 y: 345.4511
547: Action moveH x: 207.1875 y: 362.2755
547: Action moveH x: 207.65625 y: 369.00528
547: Action moveH x: 208.125 y: 378.2587
547: Action moveH x: 209.53125 y: 397.60675
547: Action moveH x: 209.53125 y: 400.97162
547: Action moveH x: 210.0 y: 404.3365
547: Action moveH x: 210.0 y: 407.7014
547: Action moveH x: 210.0 y: 411.90747
547: Action moveH x: 205.78125 y: 345.4511
547: Action moveH x: 207.1875 y: 362.2755
547: Action moveH x: 207.65625 y: 369.00528
547: Action moveH x: 208.125 y: 378.2587
547: Action moveH x: 209.53125 y: 397.60675
547: Action moveH x: 209.53125 y: 400.97162
547: Action moveH x: 210.0 y: 404.3365
547: Action moveH x: 210.0 y: 407.7014
547: Action moveH x: 210.0 y: 411.90747
547: Action moveH x: 205.78125 y: 345.4511
547: Action moveH x: 207.1875 y: 362.2755
547: Action moveH x: 207.65625 y: 369.00528
547: Action moveH x: 208.125 y: 378.2587
547: Action moveH x: 209.53125 y: 397.60675
551: Action moveH x: 209.53125 y: 400.97162
551: Action moveH x: 210.0 y: 404.3365
551: Action moveH x: 210.0 y: 407.7014
551: Action moveH x: 210.0 y: 411.90747
551: Action moveH x: 205.78125 y: 345.4511
551: Action moveH x: 207.1875 y: 362.2755
551: Action moveH x: 207.65625 y: 369.00528
551: Action moveH x: 208.125 y: 378.2587
551: Action moveH x: 209.53125 y: 397.60675
551: Action moveH x: 209.53125 y: 400.97162
551: Action moveH x: 210.0 y: 404.3365
551: Action moveH x: 210.0 y: 407.7014
551: Action moveH x: 210.0 y: 411.90747
551: Action moveC [add point] x: 200 y: 400
551: [[Point(100, 100), Point(100, 200), Point(100, 300), Point(100, 400)], [Point(200, 100), Point(200, 200), Point(200, 400)]]
719: Action moveE x: 210.0 y: 424.52576
719: Action moveH x: 210.46875 y: 415.27234
719: Action moveH x: 210.46875 y: 418.63724
719: Action moveH x: 210.46875 y: 421.1609
This is my doDraw (like onDraw):
public void doDraw(Canvas canvas)
{
PathPoint xya = null;
canvas.drawColor(Color.WHITE);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
int xPos = j * mNodeGap;
int yPos = i * mNodeGap;
try {
xya = new PathPoint(xPos, yPos, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mNodeCoordinates[i][j] = xya;
canvas.drawBitmap(mBitmap, xPos, yPos, null);
}
}
synchronized (mViewThread.getSurefaceHolder())
{
//draw path
for (Path path : mGraphics)
{
float aStartCoordinates[] = {0f, 0f};
float aEndCoordinates[] = {0f, 0f};
//get path values
PathMeasure pm = new PathMeasure(path, true);
pm.getPosTan(0f, aStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
pm.getPosTan(pm.getLength(), aEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((aStartCoordinates[0] >= 1 && aStartCoordinates[1] >= 1) && (aEndCoordinates[0] >= 1 && aEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (PathPoint nodeHit : nodePattern)
{
canvas.drawBitmap(mDotOK, nodeHit.x - ((mDotOK.getWidth()/2) - (mBitmap.getWidth()/2)), nodeHit.y - ((mDotOK.getHeight()/2) - (mBitmap.getHeight()/2)), null);
}
}
}
}
And the onTouchEvent:
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (mViewThread.getSurefaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
System.out.println("Action downE x: " + event.getX() + " y: " + event.getY());
for (int i = 0; i < mGridSize; i++)
{
for (int j = 0; j < mGridSize; j++)
{
PathPoint pathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - pathPoint.x) <= 35) && (Math.abs((int)event.getY() - pathPoint.y) <= 35))
{
//mPath.moveTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//System.out.println("Action down x: " + pathPoint.x + " y: " + pathPoint.y);
ArrayList<PathPoint> newNodesPattern = new ArrayList<PathPoint>();
mNodesHitPatterns.add(newNodesPattern);
//mNodesHitPatterns.add(nh);
// pathPoint.setAction("down");
break;
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_MOVE)
{
final int historySize = event.getHistorySize();
System.out.println("Action moveE x: " + event.getX() + " y: " + event.getY());
coordinateFound:
for (int i = 0; i < mGridSize; i++)
{
for (int j = 0; j < mGridSize; j++)
{
PathPoint pathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - pathPoint.x) <= 35) && (Math.abs((int)event.getY() - pathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!pathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(pathPoint);
System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(pathPoint);
System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
else //no current match => try historical
{
if(historySize > 0)
{
for (int k = 0; k < historySize; k++)
{
System.out.println("Action moveH x: " + event.getHistoricalX(k) + " y: " + event.getHistoricalY(k));
if((Math.abs((int)event.getHistoricalX(k) - pathPoint.x) <= 35) && (Math.abs((int)event.getHistoricalY(k) - pathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!pathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(pathPoint);
System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(pathPoint);
System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
}
}
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_UP)
{
for (int i = 0; i < mGridSize; i++) {
for (int j = 0; j < mGridSize; j++) {
PathPoint pathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - pathPoint.x) <= 35) && (Math.abs((int)event.getY() - pathPoint.y) <= 35))
{
//the location of the node
//mPath.lineTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//System.out.println("Action up x: " + pathPoint.x + " y: " + pathPoint.y);
//mGraphics.add(mPath);
// mNodesHit.add(pathPoint);
// pathPoint.setAction("up");
break;
}
}
}
}
System.out.println(mNodesHitPatterns.toString());
//create mPath
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (int i = 0; i < nodePattern.size(); i++)
{
if(i == 0) //first node in pattern
{
mPath.moveTo(nodePattern.get(i).x + mBitmap.getWidth() / 2, nodePattern.get(i).y + mBitmap.getHeight() / 2);
}
else
{
mPath.lineTo(nodePattern.get(i).x + mBitmap.getWidth() / 2, nodePattern.get(i).y + mBitmap.getHeight() / 2);
}
mGraphics.add(mPath);
}
}
return true;
}
}
============ FURTHER INFORMATION ==========================
The questions is- why does the device behaves like that?
Grateful for any input on this,
D.

I found a tutorial regarding sigle touch: http://www.vogella.com/articles/AndroidTouch/article.html
You can implement it and run it to assure that you hardware is working.
The problem might be the way you evaluate event.getAction()
Also when you override boolean onTouch return tru if you consumed the event.

Related

Make oval NavigationBar

I want to NavigationBar with an increased height of 16-20, and an oval design at the bottom of the navigation bar, I have designed it on paint code and created a UIBezierPath, tried to add it on navigation bar but the result is not so good,
Paint code
public class StyleKit : NSObject {
//// Drawing Methods
#objc dynamic public class func drawOvalNavigationBarView(frame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 66)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
//// Color Declarations
let color = UIColor(red: 0.647, green: 0.824, blue: 0.475, alpha: 1.000)
//// Shadow Declarations
let shadow = NSShadow()
shadow.shadowColor = UIColor.black.withAlphaComponent(0.35)
shadow.shadowOffset = CGSize(width: 0, height: 5)
shadow.shadowBlurRadius = 5
//// Bezier Drawing
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: frame.minX + 0.00000 * frame.width, y: frame.minY + 0.00000 * frame.height))
bezierPath.addLine(to: CGPoint(x: frame.minX + 1.00000 * frame.width, y: frame.minY + 0.00000 * frame.height))
bezierPath.addLine(to: CGPoint(x: frame.minX + 1.00000 * frame.width, y: frame.minY + 0.66667 * frame.height))
bezierPath.addLine(to: CGPoint(x: frame.minX + 0.85000 * frame.width, y: frame.minY + 0.66667 * frame.height))
bezierPath.addCurve(to: CGPoint(x: frame.minX + 0.75000 * frame.width, y: frame.minY + 0.90909 * frame.height), controlPoint1: CGPoint(x: frame.minX + 0.85000 * frame.width, y: frame.minY + 0.66667 * frame.height), controlPoint2: CGPoint(x: frame.minX + 0.83500 * frame.width, y: frame.minY + 0.90909 * frame.height))
bezierPath.addCurve(to: CGPoint(x: frame.minX + 0.25000 * frame.width, y: frame.minY + 0.90909 * frame.height), controlPoint1: CGPoint(x: frame.minX + 0.72875 * frame.width, y: frame.minY + 0.90909 * frame.height), controlPoint2: CGPoint(x: frame.minX + 0.27375 * frame.width, y: frame.minY + 0.90909 * frame.height))
bezierPath.addCurve(to: CGPoint(x: frame.minX + 0.15000 * frame.width, y: frame.minY + 0.66667 * frame.height), controlPoint1: CGPoint(x: frame.minX + 0.15500 * frame.width, y: frame.minY + 0.90909 * frame.height), controlPoint2: CGPoint(x: frame.minX + 0.15000 * frame.width, y: frame.minY + 0.66667 * frame.height))
bezierPath.addLine(to: CGPoint(x: frame.minX + 0.00000 * frame.width, y: frame.minY + 0.66667 * frame.height))
bezierPath.addLine(to: CGPoint(x: frame.minX + 0.00000 * frame.width, y: frame.minY + 0.00000 * frame.height))
bezierPath.close()
context.saveGState()
context.setShadow(offset: shadow.shadowOffset, blur: shadow.shadowBlurRadius, color: (shadow.shadowColor as! UIColor).cgColor)
color.setFill()
bezierPath.fill()
context.restoreGState()
}
//// Generated Images
#objc dynamic public class func imageOfOvalNavigationBarView(imageSize: CGSize = CGSize(width: 200, height: 66)) -> UIImage {
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
StyleKit.drawOvalNavigationBarView(frame: CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height))
let imageOfOvalNavigationBarView = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return imageOfOvalNavigationBarView
}
}

how to create a view like this shape in swift?

how to create this 2D look
How to I create this.
Thanks a lot.
Try this
class TwoDView : UIView {
public var xDiff : CGFloat = 5.0
public var yDiff : CGFloat = 5.0
override func draw(_ rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
ctx?.move(to: CGPoint(x: 0, y: rect.height - yDiff))
ctx?.addLines(between: [
CGPoint(x: rect.width - xDiff, y: rect.height - yDiff),
CGPoint(x: rect.width, y: rect.height),
CGPoint(x: xDiff, y: rect.height),
CGPoint(x: 0, y: rect.height - yDiff)
])
ctx?.setFillColor(UIColor.black.cgColor)
ctx?.fillPath()
ctx?.move(to: CGPoint(x: rect.width - xDiff, y: 0))
ctx?.addLines(between: [
CGPoint(x: rect.width, y: yDiff),
CGPoint(x: rect.width, y: rect.height),
CGPoint(x: rect.width - xDiff, y: rect.height - yDiff),
CGPoint(x: rect.width - xDiff, y: 0)
])
ctx?.setFillColor(UIColor.gray.cgColor)
ctx?.fillPath()
UIImage(named: "Image")?.draw(in: CGRect(x: 0, y: 0, width: rect.width - xDiff, height: rect.height - yDiff))
}
}

how to draw UIBezierPaths swift

Here's what I want to do:
with help UIBezierPath i want draw mask like on this link
// Create an image context containing the original UIImage.
let maskWithHole = CAShapeLayer()
let biggerRect = CGRect(x: 0, y: 0, width: withSize.width, height: withSize.height)
let maskPath = UIBezierPath() //UIBezierPath(roundedRect: toRectArray.first!, cornerRadius: 40)
maskPath.move(to: CGPoint(x: biggerRect.minX, y: biggerRect.minY))
maskPath.addLine(to: CGPoint(x: biggerRect.minX, y: biggerRect.maxY))
maskPath.addLine(to: CGPoint(x: biggerRect.maxX, y: biggerRect.maxY))
maskPath.addLine(to: CGPoint(x: biggerRect.maxX, y: biggerRect.minY))
maskPath.addLine(to: CGPoint(x: biggerRect.minX, y: biggerRect.minY))
let smallerRect = CGRect(x: 10, y: 10, width: withSize.width - 10, height: withSize.height - 10)
maskPath.move(to: CGPoint(x: smallerRect.minX, y: smallerRect.minY))
maskPath.addLine(to: CGPoint(x: smallerRect.minX, y: smallerRect.maxY))
maskPath.addLine(to: CGPoint(x: smallerRect.maxX, y: smallerRect.maxY))
maskPath.addLine(to: CGPoint(x: smallerRect.maxX, y: smallerRect.minY))
maskPath.addLine(to: CGPoint(x: smallerRect.minX, y: smallerRect.minY))
maskWithHole.path = maskPath.cgPath
maskWithHole.fillRule = kCAFillRuleEvenOdd
maskWithHole.fillColor = UIColor.orange.cgColor
maskWithHole.opacity = 1.0
it's work i cut inside content of image but want round rect inside when cut like link image.

UIBezierPath Triangle reverse

Currently I am having a small issue. The code below will output an triangle shaped layer on top of my tableViewHeader but I want to reverse the cut direction of the bazier path.
Code:
let cutDirection = UIBezierPath()
cutDirection.move(to: CGPoint(x: 0, y: 0))
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: 0))
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height))
cutDirection.addLine(to: CGPoint(x: 0, y: headerRect.height - headerCut))
newHeaderLayer.path = cutDirection.cgPath
Outputs:
Result I am looking for:
Thanks for any help!
Kevin.
Try this:
let cutDirection = UIBezierPath()
cutDirection.move(to: CGPoint(x: 0, y: 0))
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: 0))
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height - headerCut))
cutDirection.addLine(to: CGPoint(x: 0, y: headerRect.height))
newHeaderLayer.path = cutDirection.cgPath
If I follow this correctly you should swap the following:
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height))
cutDirection.addLine(to: CGPoint(x: 0, y: headerRect.height - headerCut))
To:
cutDirection.addLine(to: CGPoint(x: 0, y: headerRect.height - headerCut))
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height))
Fixed it by doing it the old school way: On Paper!
Solution:
let cutDirection = UIBezierPath()
cutDirection.move(to: CGPoint(x: 0, y: 0))
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: 0))
cutDirection.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height - headerCut))
cutDirection.addLine(to: CGPoint(x: 0, y: headerRect.height))
cutDirection.close()
newHeaderLayer.path = cutDirection.cgPath

How can I create arc shape ios swift

I have an application with some buttons. I want to create a arc shape button like the following image
How can I do it? I use the following code to achieve this.
button.layer.cornerRadius = button.bounds.size.height
and
button.layer.cornerRadius = 0.5*button.bounds.size.height
I also tried this with the using of width
button.layer.cornerRadius = button.bounds.size.width
and
button.layer.cornerRadius = 0.5*button.bounds.size.width
How can I do it? Please someone help me to solve this.
To create arc shape you need to use UIBezierPath
please read apple's UIBezierPath_class Document.
They have mention method for create arc shape.
Creates and returns a new UIBezierPath object initialized with an arc of a circle.
For constructing the path
Appends an arc to the receiver’s path.
You can also Refer Core Graphics Tutorial Part 1 With Example and search with arc keyword on browser you will redirect to arc shape on the page.
Another image posted by #Jack in above comments and asked help, here is the image:
I couldn't answer him as comment, so pasting my playground here instead.
let shapeView = UIView(frame: CGRect(x: 0, y: 0, width: 360, height: 60))
let shapeSize = shapeView.frame.size
shapeView.backgroundColor = UIColor.white
let path = CGMutablePath()
path.move(to: CGPoint.zero)
let curveWidthOneFourth = shapeSize.width / 4
let curveHeight = shapeSize.height * 0.2
path.addCurve(to: CGPoint(x: curveWidthOneFourth * 2, y: curveHeight), control1: .zero, control2: CGPoint(x: curveWidthOneFourth, y: curveHeight))
path.addCurve(to: CGPoint(x: shapeSize.width, y: 0), control1: CGPoint(x: curveWidthOneFourth * 3, y: curveHeight), control2: CGPoint(x: shapeSize.width, y: 0))
path.addLine(to: CGPoint(x: shapeSize.width, y: shapeSize.height))
path.addLine(to: CGPoint(x: 0, y: shapeSize.height))
path.addLine(to: CGPoint.zero)
And add our path to a view's layer:
let layer = CAShapeLayer()
layer.path = path
layer.fillColor = UIColor.red.cgColor
shapeView.layer.addSublayer(layer)
Encountered a similar task today, I used CGMutableShape instead of UIBezierPath because I was required to animation the shape.
Anyways, to understand the shape I divided the shape into 6 parts (lines & curves):
Base UIView to draw our shape in
let shapeView = UIView(frame: CGRect(x: 16, y: 16, width: 100, height: 44))
shapeView.backgroundColor = UIColor.lightGray
self.view.addSubview(shapeView)
Actual shape and its path
let path = CGMutablePath()
path.move(to: CGPoint(x: 10, y: 0)) // Move path to beginning of part-1
// Draw above divided parts
path.addLine(to: CGPoint(x: 90, y: 0)) // Part-1
path.addCurve(to: CGPoint(x: 95, y: 22), control1: CGPoint(x: 93, y: 6.6), control2: CGPoint(x: 95, y: 14)) // Part-2
path.addCurve(to: CGPoint(x: 90, y: 44), control1: CGPoint(x: 95, y: 30), control2: CGPoint(x: 93, y: 37)) // Part-3
path.addLine(to: CGPoint(x: 10, y: 44)) // Part-4
path.addCurve(to: CGPoint(x: 5, y: 22), control1: CGPoint(x: 7, y: 37), control2: CGPoint(x: 5, y: 30)) // Part-5
path.addCurve(to: CGPoint(x: 10, y: 0), control1: CGPoint(x: 5, y: 14), control2: CGPoint(x: 6, y: 6.6)) // Part-6
let arcLayer = CAShapeLayer()
arcLayer.path = path
arcLayer.fillColor = UIColor.gray.cgColor
shapeView.layer.insertSublayer(arcLayer, at: 0)

Resources