I'm working through Apple's Swift Programming Language book and came across the following example. I want to make sure I have the concept correct before continuing.
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
The book states "the moveByX function creates a brand new structure who's x and y values are to the target location."
So, if I do this;
var myPoint = Point(x: 1, y: 1)
myPoint.moveByX(2, deltaY: 2)
My understanding is Swift releases the myPoint struct with the values 1, 1 from memory, and it is no longer available. In its place a new one is created with the values 3, 3. Am I right?
To be precise values 1 and 1 are replaced by 3, 3. Location in memory is the same in both cases and allocated when particular instance is allocated.
Related
I am trying to plot points on lineChartView class. I then plotted data on it, but I see a very funny thing. The second plot is labeled, but not the first one.
I am setting up LineChartView instance and named it lineChart:
var lineChart: LineChartView = {
var l = LineChartView()
l.translatesAutoresizingMaskIntoConstraints = false
l.backgroundColor = .white
return l
}()
lineChartDataPoints holds ChartDataEntry classes, which holds x and y values:
var lineChartDataPoints: [ChartDataEntry] = []
I loop over xData and append ChartDataEntry class to lineChartDataPoints. xData and yData variable hold x and y values: (Which is generated in other function and not really a point of this question)
for i in 0..<xData.count {
let data = ChartDataEntry(x: Double(i + 1), y: Double(yData[i])!)
lineChartDataPoints.append(data)
}
Then I add lineChartDataPoints to LineChartDataSet:
let lineDataSet = LineChartDataSet(values: lineChartDataPoints, label: "Values")
Then lineDataSet is added to lineData after setting parameters.
lineDataSet.colors = [UIColor.red]
lineDataSet.lineWidth = 5
lineDataSet.circleColors = [UIColor.blue]
lineDataSet.circleRadius = 5
lineData.addDataSet(lineDataSet)
And this is obviously not because there is only one point. Because I tried this:
print("data \(lineChartDataPoints)")
// data [ChartDataEntry, x: 1.0, y 1.0, ChartDataEntry, x: 6.0, y 1.0]
There definitely are two points supplied for the graph to display, but I am pretty stumped on why first point won't be labeled with blue dot like second point.
Before installing the new Charts framework (Version 3.2) for Swift 5, I did not have this issue. There were some tweaks to be made in Charts framework. Installing newest patch (3.3) have solved this problem. So update to the latest version of this framework to avoid this problem.
This question is inspired by Andrew Carter's comment on a previous question about the new CGSize initializer in Swift.
The Apple Docs for CGGeometry say:
... your applications
should avoid directly reading and writing the data stored in the
CGRect data structure. Instead, use the functions described here to
manipulate rectangles and to retrieve their characteristics.
Is Apple's recommendation to not directly access the data in a CGRect still valid with Swift? Why should CGRectGetMidX, CGRectGetWidth, etc. be used in place of accessing the values of a CGRect struct directly, when these properties are now exposed with Swift's new extension on CGRect?
Consider a non-standard CGRect with a negative width and height:
var rect = CGRect(x: 0.0, y: 0.0, width: -10.0, height: -10.0)
This is a valid rectangle according to the Apple docs, as "a rectangle with an origin of [0.0, 0.0] and a size of [10.0, 10.0] is exactly equivalent to a rectangle with an origin of [10.0, 10.0] and a size of [-10.0, -10.0]."
You can standardize this CGRect by calling the legacy inline CGRectStandardize method like in Objective-C, or any of the new methods provided on the Swift extension of CGRect:
CGRectStandardize(rect) // {x -10 y -10 w 10 h 10}
rect.standardized // {x -10 y -10 w 10 h 10}
rect.standardizeInPlace() // {x -10 y -10 w 10 h 10}
But wait! This will reposition your rect on the coordinate plane, not only making your width and height positive, but making your origin negative to reflect the initial position of the rect with its negative width and height.
The inline CGRectGet functions provide an interface to normalize a specific value of your rect, without changing its origin. Swift provides an extension on CGRect so you can access the normalized values directly, rather than using the legacy C methods provided by CGGeometry:
var rect = CGRect(x: 0.0, y: 0.0, width: -10.0, height: -10.0)
rect.size.width // non-normalized, returns -10
CGRectGetWidth(rect) // bridged C function, normalized, returns 10
rect.width // new from Swift extension on CGRect, normalized, returns 10
The new interfaces:
extension CGRect {
// ...
public var width: CGFloat { get }
public var height: CGFloat { get }
public var minX: CGFloat { get }
public var midX: CGFloat { get }
public var maxX: CGFloat { get }
public var minY: CGFloat { get }
public var midY: CGFloat { get }
public var maxY: CGFloat { get }
// ...
}
So the answer is yes, the same rules for CGRect in Objective-C apply in Swift as well. The only difference here is that Swift provides an extension on some CGGeometry structs which allow you to move away from the old inline C functions bridged from the CGGeometry headers.
In Swift 3, it looks like CGRectGetWidth() is replaced by CGRect.width.
I am trying to use this algorithm to give me a Fast Fourier Transform. My compiler is choking on the sqrt() function inside vDSP_vsmul() function when i copy it over. line 41 of enter link description here
The error says cannot find overload for sqrt that accepts argument list of type '([(Float)])'. Anyone know what this part of the function is trying to do? The code appears to be trying to take the square root of an array of floats which seems very odd and I can only assume it once was able to compile prior to ios 8.4 as that Surge library is pretty heavily starred. The function looks like:
import Accelerate
// MARK: Fast Fourier Transform
public func fft(input: [Float]) -> [Float] {
var real = [Float](input)
var imaginary = [Float](count: input.count, repeatedValue: 0.0)
var splitComplex = DSPSplitComplex(realp: &real, imagp: &imaginary)
let length = vDSP_Length(floor(log2(Float(input.count))))
let radix = FFTRadix(kFFTRadix2)
let weights = vDSP_create_fftsetup(length, radix)
vDSP_fft_zip(weights, &splitComplex, 1, length, FFTDirection(FFT_FORWARD))
var magnitudes = [Float](count: input.count, repeatedValue: 0.0)
vDSP_zvmags(&splitComplex, 1, &magnitudes, 1, vDSP_Length(input.count))
var normalizedMagnitudes = [Float](count: input.count, repeatedValue: 0.0)
vDSP_vsmul(sqrt(magnitudes), 1, [2.0 / Float(input.count)], &normalizedMagnitudes, 1, vDSP_Length(input.count))
vDSP_destroy_fftsetup(weights)
return normalizedMagnitudes
}
Magnitudes is an array [Float], but sqrt accepts only single Float. If you need to run sqrt on the whole array, you need to use map or one of the Accelerate methods.
Update: It looks like the sample code in the original post comes from a custom framework, which has its own sqrt([Float]) -> [Float].
magnitudes is the array of floats.
e.g [1.6, 3.6]
In swift -> sqrt(Double) accepts parameter type Double not Array.
e.g sqrt(25.0)
Problem is you are passing Array to sqrt. sqrt([25.0, 36.0])
I'm trying to track down the errors in this github project.
https://github.com/gpbl/SwiftChart
The owner doesn't seem to answer any questions or respond.
I can't get this example to run:
// Create a new series specifying x and y values
let data = [(x: 0, y: 0), (x: 0.5, y: 3.1), (x: 1.2, y: 2), (x: 2.1, y: -4.2), (x: 2.6, y: 1.1)]
let series = new ChartSeries(data)
chart.addSerie(series)
Xcode gives this error in regards to the data
ViewController.swift:41:31: '(Double, Double)' is not identical to 'Float'
in the main file Chart.swift, there is this section of code
var labels: [Float]
if xLabels == nil {
// Use labels from the first series
labels = series[0].data.map( { (point: ChartPoint) -> Float in
return point.x } )
}
else {
labels = xLabels!
}
I'm not quite sure how to deal with the map .
There are typos in the realm. It should read
let data = [(x: 0, y: 0), (x: 0.5, y: 3.1), (x: 1.2, y: 2), (x: 2.1, y: -4.2), (x: 2.6, y: 1.1)]
let series = ChartSeries(data)
chart.addSeries(series)
That being said, Swift by default infers 0.5 to be a Double, and his default init is looking for x and y to be of type Float.
I forked the repository, and added an init that will convert the doubles to float. This could obviously cause an issue if the double is too big, but for the small numbers it likely won't be an issue. I also added the example in question to the actual project. My fork is here.
I'll send a pull request if the owner wants to accept the changes. Otherwise, if I have time I may refactor it to all be Double and get rid of the extra init.
I added the following init in ChartSeries.swift, this prevents you from always having to define your array as it convert the array of Double value tuples to Floats.
init(data: Array<(x: Double, y: Double)>) {
self.data = data.map ({ (Float($0.x), Float($0.y))})
}
I am trying to write this in Swift (I am in step 54). In a UICollectionViewLayout class I have a function setup function
func setup() {
var percentage = 0.0
for i in 0...RotationCount - 1 {
var newPercentage = 0.0
do {
newPercentage = Double((arc4random() % 220) - 110) * 0.0001
println(newPercentage)
} while (fabs(percentage - newPercentage) < 0.006)
percentage = newPercentage
var angle = 2 * M_PI * (1 + percentage)
var transform = CATransform3DMakeRotation(CGFloat(angle), 0, 0, 1)
rotations.append(transform)
}
}
Here is how the setup function is described in the tutorial
First we create a temporary mutable array that we add objects to. Then
we run through our loop, creating a rotation each time. We create a
random percentage between -1.1% and 1.1% and then use that to create a
tweaked CATransform3D. I geeked out a bit and added some logic to
ensure that the percentage of rotation we randomly generate is a least
0.6% different than the one generated beforehand. This ensures that photos in a stack don't have the misfortune of all being rotated the
same way. Once we have our transform, we add it to the temporary array
by wrapping it in an NSValue and then rinse and repeat. After all 32
rotations are added we set our private array property. Now we just
need to put it to use.
When I run the app, I get a run time error in the while (fabs(percentage - newPercentage) < 0.006) line.
the setup function is called in prepareLayout()
override func prepareLayout() {
super.prepareLayout()
setup()
...
}
Without the do..while loop, the app runs fine. So I am wondering, why?
Turns out I had to be more type safe
newPercentage = Double(Int((arc4random() % 220)) - 110) * 0.0001
This must be a Swift bug. That code should NOT crash at runtime. It should either give a compiler error on the newPercentage = expression or it should correctly promote the types as C does.