In the pre-release documentation there appears to be no Swift version of CGPathApply. Is there an equivalent or alternative? I'm trying to get all subpaths of a CGPath so that I can redraw it from a different starting point.
Swift 3.0
In Swift 3.0, you can use CGPath.apply like this:
let path: CGPath = ...
// or let path: CGMutablePath
path.apply(info: nil) { (_, elementPointer) in
let element = elementPointer.pointee
let command: String
let pointCount: Int
switch element.type {
case .moveToPoint: command = "moveTo"; pointCount = 1
case .addLineToPoint: command = "lineTo"; pointCount = 1
case .addQuadCurveToPoint: command = "quadCurveTo"; pointCount = 2
case .addCurveToPoint: command = "curveTo"; pointCount = 3
case .closeSubpath: command = "close"; pointCount = 0
}
let points = Array(UnsafeBufferPointer(start: element.points, count: pointCount))
Swift.print("\(command) \(points)")
}
Swift 2.2
With the addition of #convention(c), you can now call CGPathApply directly from Swift. Here's a wrapper that does the necessary magic:
extension CGPath {
func forEach(#noescape body: #convention(block) (CGPathElement) -> Void) {
typealias Body = #convention(block) (CGPathElement) -> Void
func callback(info: UnsafeMutablePointer<Void>, element: UnsafePointer<CGPathElement>) {
let body = unsafeBitCast(info, Body.self)
body(element.memory)
}
print(sizeofValue(body))
let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer<Void>.self)
CGPathApply(self, unsafeBody, callback)
}
}
(Note that #convention(c) isn't mentioned in my code, but is used in the declaration of CGPathApply in the Core Graphics module.)
Example usage:
let path = UIBezierPath(roundedRect: CGRectMake(0, 0, 200, 100), cornerRadius: 15)
path.CGPath.forEach { element in
switch (element.type) {
case CGPathElementType.MoveToPoint:
print("move(\(element.points[0]))")
case .AddLineToPoint:
print("line(\(element.points[0]))")
case .AddQuadCurveToPoint:
print("quadCurve(\(element.points[0]), \(element.points[1]))")
case .AddCurveToPoint:
print("curve(\(element.points[0]), \(element.points[1]), \(element.points[2]))")
case .CloseSubpath:
print("close()")
}
}
(Hint: if you have to support an iOS before iOS 11, use the accepted answer. If you can require iOS 11, this answer is much easier.)
Since iOS 11, there is an official answer from Apple to this question: CGPath.applyWithBlock(_:).
This makes all the dirty tricks unnecessary that come from the problem that CGPath.apply(info:function:) is a C function that does not allow information transported in and out of the function in a usual swifty way.
The following code allows you to do:
let pathElements = path.pathElements()
To be able to do that, copy & paste
import CoreGraphics
extension CGPath {
func pathElements() -> [PathElement] {
var result = [PathElement]()
self.applyWithBlock { (elementPointer) in
let element = elementPointer.pointee
switch element.type {
case .moveToPoint:
let points = Array(UnsafeBufferPointer(start: element.points, count: 1))
let el = PathElement.moveToPoint(points[0])
result.append(el)
case .addLineToPoint:
let points = Array(UnsafeBufferPointer(start: element.points, count: 1))
let el = PathElement.addLineToPoint(points[0])
result.append(el)
case .addQuadCurveToPoint:
let points = Array(UnsafeBufferPointer(start: element.points, count: 2))
let el = PathElement.addQuadCurveToPoint(points[0], points[1])
result.append(el)
case .addCurveToPoint:
let points = Array(UnsafeBufferPointer(start: element.points, count: 3))
let el = PathElement.addCurveToPoint(points[0], points[1], points[2])
result.append(el)
case .closeSubpath:
result.append(.closeSubpath)
#unknown default:
fatalError()
}
}
return result
}
}
public enum PathElement {
case moveToPoint(CGPoint)
case addLineToPoint(CGPoint)
case addQuadCurveToPoint(CGPoint, CGPoint)
case addCurveToPoint(CGPoint, CGPoint, CGPoint)
case closeSubpath
}
or take this code as an example to how to use CGPath.applyWithBlock(_:) yourself.
For completeness, this is the official documentation from Apple: https://developer.apple.com/documentation/coregraphics/cgpath/2873218-applywithblock
Since iOS 13 there is an even more elegant official answer from Apple: Use SwiftUI (even if your UI isn't in SwiftUI)
Transform your cgPath into a SwiftUI Path
let cgPath = CGPath(ellipseIn: rect, transform: nil)
let path = Path(cgPath)
path.forEach { element in
switch element {
case .move(let to):
break
case .line(let to):
break
case .quadCurve(let to, let control):
break
case .curve(let to, let control1, let control2):
break
case .closeSubpath:
break
}
}
Variable element is of type Path.Element which is a pure Swift Enum, so there aren't even tricks necessary to get the values out of element.
For completeness, this is th official Apple documentation: https://developer.apple.com/documentation/swiftui/path/3059547-foreach
Here's the highlights from Ole Begemann's great post (thanks #Gouldsc!), adapted for Swift 3, which allows for accessing the individual elements composing a UIBezierPath instance:
extension UIBezierPath {
var elements: [PathElement] {
var pathElements = [PathElement]()
withUnsafeMutablePointer(to: &pathElements) { elementsPointer in
cgPath.apply(info: elementsPointer) { (userInfo, nextElementPointer) in
let nextElement = PathElement(element: nextElementPointer.pointee)
let elementsPointer = userInfo!.assumingMemoryBound(to: [PathElement].self)
elementsPointer.pointee.append(nextElement)
}
}
return pathElements
}
}
public enum PathElement {
case moveToPoint(CGPoint)
case addLineToPoint(CGPoint)
case addQuadCurveToPoint(CGPoint, CGPoint)
case addCurveToPoint(CGPoint, CGPoint, CGPoint)
case closeSubpath
init(element: CGPathElement) {
switch element.type {
case .moveToPoint: self = .moveToPoint(element.points[0])
case .addLineToPoint: self = .addLineToPoint(element.points[0])
case .addQuadCurveToPoint: self = .addQuadCurveToPoint(element.points[0], element.points[1])
case .addCurveToPoint: self = .addCurveToPoint(element.points[0], element.points[1], element.points[2])
case .closeSubpath: self = .closeSubpath
}
}
}
Dmitry Rodionov has produced a function for converting a Swift function to a CFunctionPointer (see https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift).
#define kObjectFieldOffset sizeof(uintptr_t)
struct swift_func_object {
uintptr_t *original_type_ptr;
#if defined(__x86_64__)
uintptr_t *unknown0;
#else
uintptr_t *unknown0, *unknown1;
#endif
uintptr_t function_address;
uintptr_t *self;
};
uintptr_t _rd_get_func_impl(void *func)
{
struct swift_func_object *obj = (struct swift_func_object *)*(uintptr_t *)(func + kObjectFieldOffset);
//printf("-->Address of C-Func %lx unk=%lx ori=%lx<--\n", obj->function_address, obj->unknown0, obj->original_type_ptr);
return obj->function_address;
}
I am using this successfully with CGPathApply along with a Swift callback function. (code at http://parker-liddle.org/CGPathApply/CGPathApply.zip)
Although as Dmitry says this is a reverse engineered function and not a supported one.
Related
I am trying to extract programmatically the names of the enum cases of UIBlurEffect.Style which have a rawValue of Int not String. The names in an array would be ["extraLight","light","dark","regular",...]
Doing print(UIBlurEffect.Style.systemChromeMaterialLight) doesn't print systemChromeMaterialLight instead it prints UIBlurEffectStyle
I tried also using Mirror but this yields a name of __C.UIBlurEffectStyle
Example code of what I am trying to do:
let myStyles : [UIBlurEffect.Style] = [.light, .dark, .regular, .prominent]
for style in myStyles {
print(style) // does not work, produces "UIBlurEffectStyle"
myFunction(styleName: String(reflecting: style)) // does not work, produces "UIBlurEffectStyle"
myFunction(styleName: String(describing: style)) // does not work, produces "UIBlurEffectStyle"
myFunction(styleName: "\(style)") // does not work, produces "UIBlurEffectStyle"
}
I am using Swift 5, iOS 14, and Xcode 12.3
For reference, the enum is defined as follows by Apple:
extension UIBlurEffect {
#available(iOS 8.0, *)
public enum Style : Int {
case extraLight = 0
case light = 1
case dark = 2
#available(iOS 10.0, *)
case regular = 4
...
Are you doing something dynamic that is related to the name on your app so you can show the correct one based on the selection?
If you are, I suggest you create your own local String enum and then add a var or function to get the blur from it instead of trying to reverse this.
But if you really, really need this for some other reason, there is a workaround, which I do not recommend, but it's here in case you want to test it out:
let blurStyle = String(describing: UIBlurEffect(style: .systemMaterialDark))
let style = blurStyle.components(separatedBy: "style=").last?.replacingOccurrences(of: "UIBlurEffectStyle", with: "")
print(style) // SystemMaterialDark
Creating your own app Style enum:
enum AppBlurStyle: String {
case extraLight
case dark
case light
case regular
var blurEffectStyle: UIBlurEffect.Style {
switch self {
case .extraLight: UIBlurEffect.Style.extraLight
case .dark: UIBlurEffect.Style.dark
case .light: UIBlurEffect.Style.light
case .regular: UIBlurEffect.Style.regular
}
}
var blurEffect: UIBlurEffect {
switch self {
case .extraLight: UIBlurEffect(style: .extraLight)
case .dark: UIBlurEffect(style:.dark)
case .light: UIBlurEffect(style:.light)
case .regular: UIBlurEffect(style:.regular)
}
}
}
Or you can even just extend UIBlurEffect.Style and add a name property, mapping them individually:
extension UIBlurEffect.Style {
var name: String {
switch self {
case .extraLight: "extraLight"
case .dark: "dark"
case .light: "light"
case .regular: "regular"
...
}
}
}
I have a file path ...
/acme101/acmeX100/acmeX100.008.png
I can use this to get the extension .png in this case
let leftSide = (lhs.fnName as NSString).pathExtension
And this to get the filename acmeX100
let leftSide = (lhs.fnName as NSString).lastPathComponent
But I want the bit in the middle... the 008 in this case?
Is there a nice one liner?
Assuming the filepath takes that general form then this is (almost) a one-liner (I like to play it safe):
var filePath = "/acme101/acmeX100/acmeX100.008.png"
func extractComponentBetweenDots(inputString: String) -> String? {
guard inputString.components(separatedBy: ".").count > 2 else { print("Incorrect format") ; return nil } // Otherwise not in the correct format, you caa add other tests
return inputString.components(separatedBy: ".")[inputString.components(separatedBy: ".").count - 2]
}
Use as follows:
if let extractedString : String = extractComponentBetweenDots(inputString: filePath) {
print(extractedString)
}
I wanted to make an example using the same technique as in your question - despite the fact that the downcasting to NSString makes the whole thing rather ugly, it works efficiently. This is in Swift 3 but it would be easy to port it back to Swift 2 if needed.
func getComponents(from str: String) -> (name: String, middle: String, ext: String) {
let compo = (str as NSString).lastPathComponent as NSString
let ext = compo.pathExtension
let temp = compo.deletingPathExtension as NSString
let middle = temp.pathExtension
let file = temp.deletingPathExtension
return (name: file, middle: middle, ext: ext)
}
let result = getComponents(from: "/acme101/acmeX100/acmeX100.008.png")
print(result.name) // "acmeX100"
print(result.middle) // "008"
print(result.ext) // "png"
If you only need the middle part:
func pluck(str: String) -> String {
return (((str as NSString).lastPathComponent as NSString).deletingPathExtension as NSString).pathExtension
}
pluck(str: "/acme101/acmeX100/acmeX100.008.png") // "008"
Bon,
Sparky thanks for your answer. I ended up with this .. which is the same and yet different.
func pluck(str:String) -> String {
if !str.isEmpty {
let bitZero = str.characters.split{$0 == "."}.map(String.init)
if (bitZero.count > 2) {
let bitFocus = bitZero[1]
print("bitFocus \(bitFocus)")
return(bitFocus)
}
}
return("")
}
So what I'm trying to do is call a function, that will run only 1 function out of 4 possible functions, so it randomly decides which one to do.
In this case those 4 functions that I'm trying to have randomly be chosen are moveUp() moveDown() moveRight() and moveLeft().
This is what I've got right now and its not really working out well. I haven't found anything to help.
func moveComputerPlayer() {
//This is where I have no idea what to do.
"randomly choose to run: moveRight(), moveLeft(), moveUp(), moveDown()
}
Thanks.
Create an array of possible functions/methods.
Select a random element.
Call the chosen function.
Remember, functions are types in Swift.
func moveUp() {}
func moveDown() {}
func moveLeft() {}
func moveRight() {}
func moveComputerPlayer() {
let moves = [
moveUp,
moveDown,
moveLeft,
moveRight,
]
let randomIndex = Int(arc4random_uniform(UInt32(moves.count)))
let selectedMove = moves[randomIndex]
selectedMove()
}
Take a look here:
https://stackoverflow.com/a/24098445/4906484
And then:
let diceRoll = Int(arc4random_uniform(4) + 1)
switch (diceRoll) {
case 1:
moveRight()
case 2:
moveLeft()
case 3:
moveUp()
case 4:
moveDown()
default:
print("Something was wrong:" + diceRoll)
}
Use arc4random() or arc4random_uniform() to generate a random number. Use e.g. switch case statement to associate number with one of the functions.
In your case:
func moveComputerPlayer() {
let rd = Int(arc4random_uniform(4) + 1)
switch rd {
case 1:
moveRight()
case 2:
moveLeft()
case 3:
moveUp()
case 4:
moveDown()
default:
print(rd)
}
}
Consider:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
How can I access, say, lefEdge's associated value, directly, without using a switch statement?
let noIdeaHowTo = leftEdge.associatedValue + 0.5
This doesn't even compile!
I had a look at these SO questions but none of the answers seem to address this issue.
The noIdeaHowTo non compiling line above should really be that one-liner, but because the associated value can be any type, I fail to even see how user code could write even a "generic" get or associatedValue method in le enum itself.
I ended up with this, but it is gross, and needs me to revisit the code each time I add/modify a case ...
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
var associatedValue: CGFloat {
get {
switch self {
case .Horizontal(let value): return value
case .Vertical(let value): return value
}
}
}
}
Any pointer anyone?
As others have pointed out, this is now kind of possible in Swift 2:
import CoreGraphics
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
}
let min = Line.Horizontal(0.0)
let mid = Line.Horizontal(0.5)
let max = Line.Horizontal(1.0)
func doToLine(line: Line) -> CGFloat? {
if case .Horizontal(let value) = line {
return value
}
return .None
}
doToLine(min) // prints 0
doToLine(mid) // prints 0.5
doToLine(max) // prints 1
You can use a guard statement to access the associated value, like this.
enum Line {
case Horizontal(Float)
case Vertical(Float)
}
let leftEdge = Line.Horizontal(0.0)
let leftMaskRightEdge = Line.Horizontal(0.05)
guard case .Horizontal(let leftEdgeValue) = leftEdge else { fatalError() }
print(leftEdgeValue)
I think you may be trying to use enum for something it was not intended for. The way to access the associated values is indeed through switch as you've done, the idea being that the switch always handles each possible member case of the enum.
Different members of the enum can have different associated values (e.g., you could have Diagonal(CGFloat, CGFloat) and Text(String) in your enum Line), so you must always confirm which case you're dealing with before you can access the associated value. For instance, consider:
enum Line {
case Horizontal(CGFloat)
case Vertical(CGFloat)
case Diagonal(CGFloat, CGFloat)
case Text(String)
}
var myLine = someFunctionReturningEnumLine()
let value = myLine.associatedValue // <- type?
How could you presume to get the associated value from myLine when you might be dealing with CGFloat, String, or two CGFloats? This is why you need the switch to first discover which case you have.
In your particular case it sounds like you might be better off with a class or struct for Line, which might then store the CGFloat and also have an enum property for Vertical and Horizontal. Or you could model Vertical and Horizontal as separate classes, with Line being a protocol (for example).
Why this is not possible is already answered, so this is only an advice. Why don't you implement it like this. I mean enums and structs are both value types.
enum Orientation {
case Horizontal
case Vertical
}
struct Line {
let orientation : Orientation
let value : CGFloat
init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
}
let x = Line(.Horizontal, 20.0)
// if you want that syntax 'Line.Horizontal(0.0)' you could fake it like this
struct Line {
let orientation : Orientation
let value : CGFloat
private init(_ orientation: Orientation, _ value: CGFloat) {
self.orientation = orientation
self.value = value
}
static func Horizontal(value: CGFloat) -> Line { return Line(.Horizontal, value) }
static func Vertical(value: CGFloat) -> Line { return Line(.Vertical, value) }
}
let y = Line.Horizontal(20.0)
You can get the associated value without using a switch using the if case let syntax:
enum Messages {
case ping
case say(message: String)
}
let val = Messages.say(message: "Hello")
if case let .say(msg) = val {
print(msg)
}
The block inside the if case let will run if the enum value is .say, and will have the associated value in scope as the variable name you use in the if statement.
With Swift 2 it's possible to get the associated value (read only) using reflection.
To make that easier just add the code below to your project and extend your enum with the EVAssociated protocol.
public protocol EVAssociated {
}
public extension EVAssociated {
public var associated: (label:String, value: Any?) {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return (associated.label!, associated.value)
}
print("WARNING: Enum option of \(self) does not have an associated value")
return ("\(self)", nil)
}
}
}
Then you can access the .asociated value with code like this:
class EVReflectionTests: XCTestCase {
func testEnumAssociatedValues() {
let parameters:[EVAssociated] = [usersParameters.number(19),
usersParameters.authors_only(false)]
let y = WordPressRequestConvertible.MeLikes("XX", Dictionary(associated: parameters))
// Now just extract the label and associated values from this enum
let label = y.associated.label
let (token, param) = y.associated.value as! (String, [String:Any]?)
XCTAssertEqual("MeLikes", label, "The label of the enum should be MeLikes")
XCTAssertEqual("XX", token, "The token associated value of the enum should be XX")
XCTAssertEqual(19, param?["number"] as? Int, "The number param associated value of the enum should be 19")
XCTAssertEqual(false, param?["authors_only"] as? Bool, "The authors_only param associated value of the enum should be false")
print("\(label) = {token = \(token), params = \(param)")
}
}
// See http://github.com/evermeer/EVWordPressAPI for a full functional usage of associated values
enum WordPressRequestConvertible: EVAssociated {
case Users(String, Dictionary<String, Any>?)
case Suggest(String, Dictionary<String, Any>?)
case Me(String, Dictionary<String, Any>?)
case MeLikes(String, Dictionary<String, Any>?)
case Shortcodes(String, Dictionary<String, Any>?)
}
public enum usersParameters: EVAssociated {
case context(String)
case http_envelope(Bool)
case pretty(Bool)
case meta(String)
case fields(String)
case callback(String)
case number(Int)
case offset(Int)
case order(String)
case order_by(String)
case authors_only(Bool)
case type(String)
}
The code above is from my project https://github.com/evermeer/EVReflection
https://github.com/evermeer/EVReflection
Given the following enum:
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
}
How to generate a random variable of type GameLevel in Swift?
I updated your enum as per Apple standards (Capital letter to start a Type, and no abbreviations.
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
}
First, how to create a constant or variable with a value for level.
let level = GameLevel.Level(1)
Next, for a random value to level use arc4random_uniform:
let maxGameLevel: UInt32 = 10
let randomGameLevel: Int = Int(arc4random_uniform(maxGameLevel))
let level = GameLevel.Level(randomGameLevel)
Of course, this can be put into a function:
func RandomGameLevel() -> GameLevel {
let maxGameLevel: UInt32 = 10
return .Level(Int(arc4random_uniform(maxGameLevel)))
}
let level = RandomGameLevel()
Finally, here is how you would use it in a case statement:
switch level {
case .Level(let levelValue):
println("Level \(levelValue)")
case .TutorialLevel:
println("Tutorial Level")
case .BossLevel:
println("Boss Level")
}
Update
OK, it's not too hard to include the other values. I'll also put all of this into GameLevel to package it up better.
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
static func Random() -> GameLevel {
let maxGameLevel: UInt32 = 10 /* levels will be 0 through 9 */
let otherGameLevels: UInt32 = 2 /* TutorialLevel and BossLevel */
let levelValue = Int(arc4random_uniform(maxGameLevel + otherGameLevels))
switch levelValue {
case 10: return .TutorialLevel
case 11: return .BossLevel
default: return .Level(levelValue)
}
}
}
Then
let level = GameLevel.Random()
Not the cleanest, but it's a start.
enum GameLevel: CaseIterable {
case Level(Int)
case TutorialLevel, BossLevel
}
let level:GameLevel = GameLevel.allCases.randomElement()!
Why would you need it that way? :(
Assign numbers to your start and final levels and implement a function, which will return random in that range as Lvl(int)