Swift 3 - Slider's pointer width - ios

I want to make a label for a slider that shows the position of the pointer like this:
This is my code that's moving this label when the slider's pointer moves:
var sliderPointWidth : CGFloat = 32.0
#IBAction func sliderValueChanged( _ slider : UISlider ){
sliderLabel.text = "\( Int( roundf( slider.value ) ) )"
let leftMove = slider.frame.minX
let allRange = ( slider.frame.width - sliderPointWidth ) * CGFloat( slider.value / slider.maximumValue )
let middleOfSliferLabel = sliderLabel.frame.width / 2
let x = leftMove + sliderPointWidth / 2 + allRange - middleOfSliferLabel
sliderLabel.frame.origin = CGPoint( x: CGFloat(x) , y: sliderLabel.frame.minY )
}
But to make the label's middle x and the pointer's middle x the same, I need to know the width of this pointer. Fluently looking in this, I understood that it's about 32 points. But I don't know this value for other screens. Are there any methods in UISlider that can say this value?

You should retreive the size for the thumb with thumbRect:
let thumbSize = slider.thumbRect(forBounds: slider.bounds, trackRect: slider.trackRect(forBounds: slider.bounds), value: 0).size

Related

Camera is not following the airplane in Scenekit

I have a flying aircraft which I am following and I am also showing the path the aircraft has followed. I am drawing cylinders as a line for drawing the path. Its kind of drawing a line between 2 points. I have a cameraNode which is set to (0,200,200) initially. At that point I can see the aircraft. But when I start my flight. It goes out of the screen. I want 2 things :
Follow just the aircraft (Path won't matter).
Show whole path and also the aircraft.
I tried finding the min ad max x,y and z and taking average but it din't work. If you see below gif its too zoomed and aircraft has moved out of the screen
Here is how I set my camera:
- (void)setUpCamera {
SCNScene *workingScene = [self getWorkingScene];
_cameraNode = [[SCNNode alloc] init];
_cameraNode.camera = [SCNCamera camera];
_cameraNode.camera.zFar = 500;
_cameraNode.position = SCNVector3Make(0, 60, 50);
[workingScene.rootNode addChildNode:_cameraNode];
SCNNode *frontCameraNode = [SCNNode node];
frontCameraNode.position = SCNVector3Make(0, 100, 50);
frontCameraNode.camera = [SCNCamera camera];
frontCameraNode.camera.xFov = 75;
frontCameraNode.camera.zFar = 500;
[_assetActivity addChildNode:frontCameraNode]; //_assetActivity is the aircraft node.
}
Here is how I am changing camera position which is not working:
- (void)showRealTimeFlightPath {
DAL3DPoint *point = [self.aircraftLocation convertCooridnateTo3DPoint];
DAL3DPoint *previousPoint = [self.previousAircraftLocation convertCooridnateTo3DPoint];
self.minCoordinate = [self.minCoordinate findMinPoint:self.minCoordinate currentPoint:point];
self.maxCoordinate = [self.minCoordinate findMaxPoint:self.maxCoordinate currentPoint:point];
DAL3DPoint *averagePoint = [[DAL3DPoint alloc] init];
averagePoint = [averagePoint averageBetweenCoordiantes:self.minCoordinate maxPoint:self.maxCoordinate];
SCNVector3 positions[] = {
SCNVector3Make(point.x,point.y,point.z) ,
SCNVector3Make(previousPoint.x,previousPoint.y,previousPoint.z)
};
SCNScene *workingScene = [self getWorkingScene];
DALLineNode *lineNodeA = [[DALLineNode alloc] init];
[lineNodeA init:workingScene.rootNode v1:positions[0] v2:positions[1] radius:0.1 radSegementCount:6 lineColor:[UIColor greenColor]] ;
[workingScene.rootNode addChildNode:lineNodeA];
self.previousAircraftLocation = [self.aircraftLocation mutableCopy];
self.cameraNode.position = SCNVector3Make(averagePoint.x, averagePoint.y, z);
self.pointOfView = self.cameraNode;
}
Code in swift or objective c are welcomed.
Thanks!!
The first behavior you describe would most easily be achieved by chaining a look-at constraint and a distance constraint, both targeting the aircraft.
let lookAtConstraint = SCNLookAtConstraint(target: aircraft)
let distanceConstraint = SCNDistanceConstraint(target: aircraft)
distanceConstraint.minimumDistance = 10 // set to whatever minimum distance between the camera and aircraft you'd like
distanceConstraint.maximumDistance = 10 // set to whatever maximum distance between the camera and aircraft you'd like
camera.constraints = [lookAtConstraint, distanceConstraint]
For iOS 10 and earlier, you can implement a distance constraint using SCNTransformConstraint. Here's a basic (though slightly ugly 😛) implementation that uses linear interpolation to update the node's position.
func normalize(_ value: Float, in range: ClosedRange<Float>) -> Float {
return (value - range.lowerBound) / (range.upperBound - range.lowerBound)
}
func interpolate(from start: Float, to end: Float, alpha: Float) -> Float {
return (1 - alpha) * start + alpha * end
}
let target = airplane
let minimumDistance: Float = 10
let maximumDistance: Float = 15
let distanceConstraint = SCNTransformConstraint(inWorldSpace: false) { (node, transform) -> SCNMatrix4 in
let distance = abs(sqrt(pow(target.position.x - node.position.x, 2) + pow(target.position.y - node.position.y, 2) + pow(target.position.z - node.position.z, 2)))
let normalizedDistance: Float
switch distance {
case ...minimumDistance:
normalizedDistance = self.normalize(minimumDistance, in: 0 ... distance)
case maximumDistance...:
normalizedDistance = self.normalize(maximumDistance, in: 0 ... distance)
default:
return transform
}
node.position.x = self.interpolate(from: target.position.x, to: node.position.x, alpha: normalizedDistance)
node.position.y = self.interpolate(from: target.position.y, to: node.position.y, alpha: normalizedDistance)
node.position.z = self.interpolate(from: target.position.z, to: node.position.z, alpha: normalizedDistance)
return transform
}
The second behavior could be implemented by determining the bounding box of your aircraft and all of its path segments in the camera's local coordinate space, then updating the camera's distance from the center of that bounding box to frame all of those nodes in the viewport. frameNodes(_:), a convenience method that implements this functionality, was introduced in iOS 11 and is defined on SCNCameraController. I'd recommend using it if possible, unless you want to dive into the trigonometry yourself. You could use your scene view's default camera controller or create a temporary instance, whichever suits the needs of your app.
You need to calculate the angle of the velocity so that the camera points in the direction of the moving SCNNode.
This code will point you in the right direction.
func renderer(_ aRenderer: SCNSceneRenderer, didSimulatePhysicsAtTime time: TimeInterval) {
// get velocity angle using velocity of vehicle
var degrees = convertVectorToAngle(vector: vehicle.chassisBody.velocity)
// get rotation of current camera on X and Z axis
let eX = cameraNode.eulerAngles.x
let eZ = cameraNode.eulerAngles.z
// offset rotation on y axis by 90 degrees
// this needs work, buggy
let ninety = deg2rad(90)
// default camera Y Euler angle facing north at 0 degrees
var eY : Float = 0.0
if degrees != 0 {
eY = Float(-degrees) - Float(ninety)
}
// rotate camera direction using cameraNode.eulerAngles and direction of velocity as eY
cameraNode.eulerAngles = SCNVector3Make(eX, eY, eZ)
// put camera 25 points behind vehicle facing direction of velocity
let dir = calculateCameraDirection(cameraNode: vehicleNode)
let pos = pointInFrontOfPoint(point: vehicleNode.position, direction:dir, distance: 25)
// camera follows driver view from 25 points behind, and 10 points above vehicle
cameraNode.position = SCNVector3Make(pos.x, vehicleNode.position.y + 10, pos.z)
}
func convertVectorToAngle(vector: SCNVector3) -> CGFloat {
let degrees = atan2(vector.z, vector.x)
return CGFloat(degrees)
}
func pointInFrontOfPoint(point: SCNVector3, direction: SCNVector3, distance: Float) -> SCNVector3 {
var x = Float()
var y = Float()
var z = Float()
x = point.x + distance * direction.x
y = point.y + distance * direction.y
z = point.z + distance * direction.z
let result = SCNVector3Make(x, y, z)
return result
}
func calculateCameraDirection(cameraNode: SCNNode) -> SCNVector3 {
let x = -cameraNode.rotation.x
let y = -cameraNode.rotation.y
let z = -cameraNode.rotation.z
let w = cameraNode.rotation.w
let cameraRotationMatrix = GLKMatrix3Make(cos(w) + pow(x, 2) * (1 - cos(w)),
x * y * (1 - cos(w)) - z * sin(w),
x * z * (1 - cos(w)) + y*sin(w),
y*x*(1-cos(w)) + z*sin(w),
cos(w) + pow(y, 2) * (1 - cos(w)),
y*z*(1-cos(w)) - x*sin(w),
z*x*(1 - cos(w)) - y*sin(w),
z*y*(1 - cos(w)) + x*sin(w),
cos(w) + pow(z, 2) * ( 1 - cos(w)))
let cameraDirection = GLKMatrix3MultiplyVector3(cameraRotationMatrix, GLKVector3Make(0.0, 0.0, -1.0))
return SCNVector3FromGLKVector3(cameraDirection)
}
func deg2rad(_ number: Double) -> Double {
return number * .pi / 180
}

How to calculate the correct position for a matrix of circles to fit inside given view with proportional gaps?

I am pretty new to iOS development and I am trying to display a 10x10 grid inside a UIView respecting its bounds and I would like that the circles would be calculated based on the available width/height of the device.
What I tried so far without luck:
func setUpPoints() {
let matrixSize = 10
let diameter = (min(painelView.frame.size.width, painelView.frame.size.height) / CGFloat(matrixSize + 1)).rounded(.down)
let radius = diameter / 2
for i in 0...matrixSize {
for j in 0...matrixSize {
let x = CGFloat(i) * diameter + radius
let y = CGFloat(j) * diameter + radius
let frame = CGRect(x: x, y: y, width: diameter, height: diameter)
let circle = Circle(frame: frame)
circle.tag = j * matrixSize + i + 1
painelView.addSubview(circle)
}
}
}
My goal is to distribute the circles inside the gray rectangle proportionally so it will look like the Android pattern lock screen:
Can someone please give me some pointers?
Thanks.
If I understand what you are trying to do, then the following line:
let radius = (painelView.frame.size.width + painelView.frame.size.height) / CGFloat(matrixSize * 2)
should be:
let radius = (min(painelView.frame.size.width, painelView.frame.size.height) / CGFloat(matrixSize + 1)).rounded(.down)
The above change will allow the "square" of circles fit within whichever is smaller - the view's width or height, allowing for a gap around the "square" equal to half the diameter of each circle.
You also need to change both loops to start with 0.
for i in 0..<matrixSize {
for j in 0..<matrixSize {
BTW - your radius variable is really the diameter. And gap is really the radius.
The following code provides a border around the square of circles and it includes some space between the circles. Adjust as needed.
func setUpPoints() {
let matrixSize = 10
let borderRatio = CGFloat(0.5) // half a circle diameter - change as desired
let gapRatio = CGFloat(0.25) // quarter circle diameter - change as desired
let squareSize = min(painelView.frame.size.width, painelView.frame.size.height)
let diameter = (squareSize / (CGFloat(matrixSize) + 2 * borderRatio + CGFloat(matrixSize - 1) * gapRatio)).rounded(.down)
let centerToCenter = (diameter + diameter * gapRatio).rounded(.down)
let borderSize = (diameter * borderRatio).rounded()
for i in 0..<matrixSize {
for j in 0..<matrixSize {
let x = CGFloat(i) * centerToCenter + borderSize
let y = CGFloat(j) * centerToCenter + borderSize
let frame = CGRect(x: x, y: y, width: diameter, height: diameter)
let circle = Circle(frame: frame)
circle.tag = j * matrixSize + i + 1
painelView.addSubview(circle)
}
}
}

Add block between two points. SpriteKit

I am trying to add a SKNode between two points like picture below.
What I have:
I count the distance between those two points with this code (works fine):
func distanceCount(_ point: CGPoint) -> CGFloat {
return abs(CGFloat(hypotf(Float(point.x - x), Float(point.y - y)))) }
Then I count the middle point(also works fine)
func middlePointCount(_ point: CGPoint) -> CGPoint {
return CGPoint(x: CGFloat((point.x + x) / 2), y: CGFloat((point.y + y) / 2))
}
Finally this function adds my object (SKNode) :
func addBlock(_ size:CGSize, rotation:CGFloat, point: CGPoint) -> SKNode{
let block = SKSpriteNode(color: UIColor.lightGray , size: size)
block.physicsBody = SKPhysicsBody(rectangleOf: block.frame.size)
block.position = point //This is my middle point
block.physicsBody!.affectedByGravity = false
block.physicsBody!.isDynamic = false
block.zRotation = rotation
return block
}
Summary: My addBlock function adds object with right width and centred on the right place , but angle is wrong.
Note: I have tried to create functions which should count the angle but they were all wrong :/ .
My question: How can I get the right angle , or is there some other how can I reach my goal?
If you need more details just let me know.
Thank you :)
To get the angle between two points you'll need to use the following
atan2(p2.y-p1.y, p2.x-p1.x)
Midpoint
The midpoint between 2 points A and B is defined as
midpoint = {(A.x + B.x) / 2, (A.y + B.y) / 2}
CGPoint Extension
So let's create and extension of CGPoint to easily build a Midpoint starting from 2 points
extension CGPoint {
init(midPointBetweenA a: CGPoint, andB b: CGPoint) {
self.x = (a.x + b.x) / 2
self.y = (a.y + b.y) / 2
}
}
Test
Now let's test it
let a = CGPoint(x: 1, y: 4)
let b = CGPoint(x: 2, y: 3)
let c = CGPoint(midPointBetweenA: a, andB: b) // {x 1,5 y 3,5}
Looks good right?
Wrap up
Now given your 2 points you just need to calculate the midpoint and assign it to the position of your SKNode.
let nodeA: SKNode = ...
let nodeB: SKNode = ...
let nodeC: SKNode = ...
nodeC.position = CGPoint(midPointBetweenA: nodeA.position, andB: nodeB.position)

Setting limits to sprite vertical location with arc4Random

I'm building my first 2D platform game and I'm looking to set some limits to objects that are generated every few seconds using arc4Random.
Currently a bird will fly across the screen from right to left and most of the time the bird appears to be in the air, however sometimes the bird it at ground level that just looks strange.
What I would like to do is to set a minimum and maximum height the birds will be generated in, is this possible?
Here is some of the code...
func spawnBird() {
var birdP = SKNode()
birdP.position = CGPointMake( self.frame.size.width + birdTexture1.size().width * 2, 0 );
birdP.zPosition = -10;
var height = UInt32( self.frame.size.height / 1 )
var y = arc4random() % height;
var bird1 = SKSpriteNode(texture: birdTexture1)
//Code Removed
birds.addChild(birdP)
You could set a minimum and maximum height:
var height_max = UInt32( self.frame.size.height )
var height_min = UInt32( 20 )
var bird_range = arc4random_uniform(height_max - height_min + 1) + height_min;
Alternate method:
var bird_range = (arc4random() % (height_max - height_min) + 1) + height_min;
Methods Graphed:
The two using max/min height never got below 20, the original method you're using often hit 0.
this is standard problem.
int randomInIntRange(int minVal, int maxVal) {
return minVal+arc4random_uniform(maxVal-minVal+1);
}
CGFloat randomInFloatRange(CGFloat minVal, CGFloat maxVal) {
return minVal+(maxVal-minVal)*(arc4random()/(double)(UINT32_MAX));
}

Drawing in custom UIView wrongly placed in iOS7 but correct in iOS8

I'm drawing rings and circles in a custom view of mine. While this works perfectly well in iOS8, in iOS7 the drawing is done on totally different origins. What am I missing here?
Displayed correctly in iOS8:
Displayed incorrectly in iOS7:
Offscreen rendering shows me, that iOS7 recognizes my frames correctly:
How I calculate the arcCenter and arcRadius:
func prepareLayerAndShape()
{
let size = self.frame.size.height >= self.frame.size.width ? self.frame.size.width : self.frame.size.height
self.layer.masksToBounds = true
self.layer.cornerRadius = size / 2
self.arcRadius = size / 2
self.arcCenter = CGPointMake(size / 2, size / 2)
}
The drawing done in drawRect:
func createBackgroundRing(graphicsContext: CGContext)
{
CGContextBeginPath(graphicsContext)
CGContextSetStrokeColorWithColor(graphicsContext, self.delegate.shapeBackgroundColor(self))
CGContextAddArc(graphicsContext, self.arcCenter.x, self.arcCenter.y, self.arcRadius - self.delegate.ringLineWidth(self) / 2, 0.0, CGFloat(2 * M_PI), 1)
CGContextStrokePath(graphicsContext)
}
func createCountdownRing(graphicsContext: CGContext)
{
CGContextBeginPath(graphicsContext)
CGContextSetStrokeColorWithColor(graphicsContext, self.delegate.shapeForegroundColor(self))
CGContextAddArc(graphicsContext, self.arcCenter.x, self.arcCenter.y, self.arcRadius - self.delegate.ringLineWidth(self) / 2, self.startAngle, self.endAngle, self.delegate.isClockwise(self))
CGContextStrokePath(graphicsContext)
}
The calculation of the position and size of the frames for my custom views done in the ViewController:
func frameForPosition(position: CGFloat) -> CGRect
{
let displacementFact: CGFloat = 1.5
let cWidth = self.viewCountdown.frame.size.width
let cHeight = self.viewCountdown.frame.size.height
let diff = cWidth > cHeight ? cWidth - cHeight : cHeight - cWidth
let size = cWidth > cHeight ? cHeight : cWidth
let x = cWidth > cHeight ? self.viewCountdown.frame.origin.x + diff / 2 : self.viewCountdown.frame.origin.x
let y = cHeight > cWidth ? self.viewCountdown.frame.origin.y + diff / 2 : self.viewCountdown.frame.origin.y
let countDownRingFrame = CGRectMake(
x + displacementFact * self.lineWidth * position,
y + displacementFact * self.lineWidth * position,
size - (displacementFact * 2.0 * self.lineWidth * position),
size - (displacementFact * 2.0 * self.lineWidth * position)
)
return countDownRingFrame
}
Side note: I started coding the project for iOS8 but had to downgrade to iOS7 afterwards.
I managed to solve this problem by deleting the ViewController in the Storyboard and the file itself and adding them both again. Apparently the downgrading was the source of the problem. The same code works just fine now.

Resources