How to create shorthands for CGPoint & CGVector? - swift

I'm doing a lot of positioning and animation stuff in literals, and they're taking up a lot of space and becoming unreadable because of the verbosity.
What I'd like to do is turn this
var xy = CGPoint(x: 100, y: 100)
Into this:
var xy = •(100, 100)
Similarly, I'd like to go from:
CGVector(dx: 200, dy: 200)
to something like this:
`(200, 200)
But I don't know how a macro-like shorthand or something like this would be done, or even if it could be done. Any pointers (puntendered) would be better than where I'm at.
I get that this is probably, for most people, less readable.
Due to the context I always know what these are. I don't need the parameter labels or function name to understand what I'm doing. And this is private animation and motion testing, there's no need for anyone else to ever understand what's going on.

extension CGPoint {
init(_ x: CGFloat, _ y: CGFloat) {
self.init(x: x, y: y)
}
}
extension CGVector {
init(_ dx: CGFloat, _ dy: CGFloat) {
self.init(dx: dx, dy: dy)
}
}
typealias P = CGPoint
typealias V = CGVector
let p = P(10, 10)
let v = V(10, 10)
But no idea about the • and ` part - I replaced them with P and V :)

For this type of thing you would likely use a typealias but I don't think you will be able to create types with names as operators or symbols.
See below, I created a typeAlias of CGP for CGPoint, you could simply just alias it to Point if you like.
Here is some information from the docs
A type alias declaration introduces a named alias of an existing type into your program. Type alias declarations are declared using the typealias keyword and have the following form:
typealias name = existing type
After a type alias is declared, the aliased name can be used instead of the existing type everywhere in your program. The existing type can be a named type or a compound type. Type aliases do not create new types; they simply allow a name to refer to an existing type.
EDIT
Two simple method of excluding the parameter names:
func cp(_ x: Int, _ y: Int) -> CGPoint {
return CGPoint(x: x, y: y)
}
let point = cp(0,2)
extension CGPoint {
init(_ x: Int, _ y: Int) {
self.init(x: x, y: y)
}
}
typealias CGP = CGPoint
let point2 = CGP(1,1)
The first method just creates a helper function which returns a CGPoint using it's designed intialiser. The second option is the better way of doing it, you extend CGPoint and add an intializer that marks the params as non-named params (by using the _ instead of the external name)

You can't use special characters as the compiler would not be able to differentiate between function names and operators like '*'.
Using typealias
using a typealias would allow you to shorten the function name but not omit the parameter names like you outlined in your question.
typealias p = CGPoint
//example
let p1 = p(x: 60, y: 60)
Using anonymous parameters
func p(_ x: CGFloat, _ y: CGFloat) -> CGPoint {
return CGPoint(x: x,y: y)
}
//example
let p1 = p(60, 60) //no parameter names

I too like to get rid of the parameter labels, but I don't advise reducing things to any briefer shorthand. I paste this code into the start of all my app projects:
extension CGRect {
init(_ x:CGFloat, _ y:CGFloat, _ w:CGFloat, _ h:CGFloat) {
self.init(x:x, y:y, width:w, height:h)
}
}
extension CGSize {
init(_ width:CGFloat, _ height:CGFloat) {
self.init(width:width, height:height)
}
}
extension CGPoint {
init(_ x:CGFloat, _ y:CGFloat) {
self.init(x:x, y:y)
}
}
extension CGVector {
init (_ dx:CGFloat, _ dy:CGFloat) {
self.init(dx:dx, dy:dy)
}
}
That allows me to say things like CGPoint(1,4), and that's good enough, in my opinion.

Related

Need explaining on the double label function declaration

I was reading the apple doc when I fell upon this piece of syntax :
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)"
Could someone explain why moveBy(x deltaX: Double, y deltaY: Double) has double labels on the arguments ?
Short answer: first argument label is for external caller, second one for local in-method use.
func moveBy(x deltaX: Double, y deltaY: Double) when calling looks following: moveBy(x: 1, y: 1), but inside the method deltaX and deltaY labels are used.
This naming style is not necessary, you can declare the method func moveBy(x: Double, y: Double) so x and y will be used inside the method.
To support legacy style, so from caller scope your method looks like moveBy(1, 1), you should place _ as first argument label: func moveBy(_ deltaX: Double, _ deltaY: Double). Such declarations are used in CocoaTouch to support legacy obj-c interface (ex. func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool).
The first label is Argument Labels and the second label is Parameter Names.
From Apple Document:
Each function parameter has both an argument label and a parameter
name. The argument label is used when calling the function; each
argument is written in the function call with its argument label
before it. The parameter name is used in the implementation of the
function. By default, parameters use their parameter name as their
argument label.
Usage:
func foo(with hoge: Int) {
return hoge * 2
}
let A = foo(with: 2) // A = 4;
Look closely to the Apple documentation.It is connected with Parameter Names.
You can override the default behavior for argument labels with one of
the following forms:
argument label parameter name: parameter type
_ parameter name: parameter type

Make simple tuple conform to Hashable, so can be a Dictionary Key

I'd like to use a very simple tuple as a key:
(Int, Int)
Dictionary keys need to be Hashable. I've learnt.
But can't find how I make this simple tuple Hashable, and do struggle with protocol conformance at the best of times.
More profoundly, a CGPoint would solve my problems. It can be of this format, but is not hashable.
Is it possible to extend a CGPoint so it's hashable? If so, how?
EDIT: Image of Int variant of CGPoint choice.
Making conform to Hashable is not difficult for class, struct or enum.
You just need to explicitly declare conformance to Hashable and define a property hashValue: Int. Practically, the hashValue needs to fulfill one simple axiom: if a == b then a.hashValue == b.hashValue.
(To conform Hashable, you also need to make the type Equatable. In case of CGPoint, it is already Equatable.)
An example to make CGPoint conform to Hashable:
extension CGPoint: Hashable {
public var hashValue: Int {
//This expression can be any of the arbitrary expression which fulfills the axiom above.
return x.hashValue ^ y.hashValue
}
}
var pointDict: [CGPoint: String] = [
CGPoint(x: 1.0, y: 2.0): "PointA",
CGPoint(x: 3.0, y: 4.0): "PointB",
CGPoint(x: 5.0, y: 6.0): "PointC",
]
print(pointDict[CGPoint(x: 1.0, y: 2.0)]) //->Optional("PointA")
As CGPoint contains CGFloat values, so, CGPoint as Key of Dictionary may cause unexpected behavior based on the calculation error of binary floating-point system. You need to use it with extra caution.
ADDITION
If you want to avoid some calculation error issue and can accept that the structure can only contain Ints, you can define your own struct and make it conform to Hashable:
struct MyPoint {
var x: Int
var y: Int
}
extension MyPoint: Hashable {
public var hashValue: Int {
return x.hashValue ^ y.hashValue
}
public static func == (lhs: MyPoint, rhs: MyPoint) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
var myPointDict: [MyPoint: String] = [
MyPoint(x: 1, y: 2): "MyPointA",
MyPoint(x: 3, y: 4): "MyPointB",
MyPoint(x: 5, y: 6): "MyPointC",
]
print(myPointDict[MyPoint(x: 1, y: 2)]) //->Optional("MyPointA")
Not much more difficult than the code above, one more thing you need is just defining == operator for the struct. Please make it a try.

Getting the initialiser values in Swift 3

Essentially I am creating a new data type called Vector3.
class Vector3 {
var x:Int
var y:Int
var z:Int
init(x:Int,y:Int,z:Int) {
self.x = x
self.y = y
self.z = z
}
}
I used the operator overloading feature in Swift in order to use operators on my custom data types.
infix operator +
func +(left:Vector3,right:Vector3) -> Vector3 {
return (Vector3(x: left.x + right.x, y: left.y + right.y, z: left.z + right.z))
}
var customVector = Vector3(x: 1, y: 2, z: 3)
var secondVector = Vector3(x: 3, y: 2, z: 1)
print(customVector + secondVector) //Take note
Currently the console is printing like this:
Vector3
Without the initialisation values, only with the class name.
However, WHAT I WANT is to print something like this:
Vector3(x:4,y:4,z:4)
Hopefully,someone can help me with this! Thanks!
NOTE:I am fully aware that one can get the values of these properties by:
customVector.x //1
customVector.y //2
Currently the console is printing like this: Vector3 without the initialisation values, only with the class name.
This is because you did not tell Swift how exactly you want your instances printed when you interpret them as strings. Make your class conformant to CustomStringConvertible protocol and implement description to print what you need:
class Vector3 : CustomStringConvertible {
...
public var description: String {
return "Vector3(x:\(x),y:\(y),z:\(z))"
}
}

Why do I keep getting "Extra Argument in Call" for my initializer in Swift

I keep getting the error "extra argument in call" for repeatedValue in the init function. Why?
class Point<T> {
/* n dimensional point
multiline comments ...
*/
let point : [T]
init(dimensions: Int, rValue: Float = 0.0){
self.point = [T](count: dimensions, repeatedValue:rValue)
}
}
The definition for init with repeatedValue is
Array<T> init(count: Int, repeatedValue: T)
Your rValue must be of type T
If you need the default value your T has also to be a FloatLiteralConvertible,
this:
Array<T> init(count: Int, repeatedValue: T)
won't do it. This however will work and makes more sense since you don`t want points made out of for example "Cats" i guess...
Solution:
class Point<T:FloatLiteralConvertible> {
/* n dimensional point
multiline comments ...
*/
let point : [T]
init(dimensions: Int, rValue: T = 0.0 ){
self.point = [T](count: dimensions, repeatedValue:rValue)
}
}
var pd = Point<Double>(dimensions: 10, rValue: 1.0)
var pf = Point<Float>(dimensions: 10, rValue: 1.0)
dump(pd.point)
dump(pf.point)

How to create enum with raw type of CGPoint?

Inspired from this question. Swift support to create enum with any raw type, so it will be nice to able to create enum with raw type of CGPoint.
But this code won't compile
enum MyEnum : CGPoint {
case Zero
}
with following error
<REPL>:50:15: error: raw type 'CGPoint' is not convertible from any literal
enum MyEnum : CGPoint {
^
<REPL>:51:10: error: enum cases require explicit raw values when the raw type is not integer literal convertible
case Zero
^
So how to declare enum with raw type of CGPoint?
There are two errors in given code.
First one is
error: raw type 'CGPoint' is not convertible from any literal
enum MyEnum : CGPoint {
So we need to make CGPoint convertible from literal
One way to solve it is to extend CGPoint to make it convertible from String literal by conform StringLiteralConvertible
extension CGPoint : StringLiteralConvertible {
static func convertFromStringLiteral(value: String) -> CGPoint {
return NSPointFromString(value) // CGPointFromString on iOS
}
static func convertFromExtendedGraphemeClusterLiteral(value: String) -> CGPoint {
return NSPointFromString(value) // CGPointFromString on iOS
}
}
we can create CGPoint from string literal now!
var p : CGPoint = "2,3"
println(p) // print (2.0,3.0)
The second error is
error: enum cases require explicit raw values when the raw type is not integer literal convertible
case Zero
^
which is easy to fix now, just assign some string literal to it
enum MyEnum : CGPoint {
case Zero = "0, 0"
case One = "1, 1"
case MagicPoint = "0, 42"
}
println(MyEnum.Zero.toRaw()) // (0.0,0.0)
println(MyEnum.One.toRaw()) // (1.0,1.0)
println(MyEnum.MagicPoint.toRaw()) // (0.0,42.0)
and now you have enum with CGPoint raw type
to use it
if let p = MyEnum.fromRaw(CGPoint(x:0, y:42)) {
switch (p) {
case .Zero:
println("p is (0, 0)")
break
case .One:
println("p is (1, 1)")
break
case .MagicPoint:
println("p is magic point")
break
}
}
// print "p is magic point"
It will be nicer to create CGPoint from tuple, however, looks like it is not possible.
From the grammar
literal → integer-literal­ floating-point-literal­ string-literal­
there are only three types of literal, so string-literal is the only option here (unless you want 1.2 to be (1, 2))
You can actually have a proper way. This is the code that allows you to have CGPoint as a RawValue of enum:
enum MyPointEnum {
case zero
}
extension MyPointEnum: RawRepresentable {
typealias RawValue = CGPoint
init?(rawValue: CGPoint) {
if rawValue == CGPoint.zero {
self = .zero
} else {
return nil
}
}
var rawValue: CGPoint {
switch self {
case .zero:
return CGPoint.zero
}
}
}
print(MyPointEnum.zero.rawValue) //prints "(0.0, 0.0)\n"
I really like Bryan Chen's solution but I came with one alternative. It doesn't really uses enums:
extension CGPoint : RawRepresentable, Equatable {
typealias RawType = (CGFloat, CGFloat)
static let Zero = CGPointZero
static let One = CGPoint(x: 1.0, y: 1.0)
static let MagicPoint = CGPoint(x: 42.0, y: 0.0)
static func fromRaw(raw: RawType) -> CGPoint? {
return CGPoint(x: raw.0, y: raw.1)
}
func toRaw() -> RawType {
return (x, y)
}
}
Now we can do all that "raw" operations:
var p = CGPoint.fromRaw((10, 20)) //from tuple
We can also use a switch:
switch (p) {
case CGPoint.Zero:
println("p is (0, 0)")
case CGPoint.One:
println("p is (1, 1)")
case CGPoint.MagicPoint:
println("p is (42, 0)")
case CGPoint(x: 0, y: 10):
println("p is (0, 10)")
default:
println("Something else")
}
However, you need the default case and you cannot use the short .Zero names.