Posts

Post marked as solved
4 Replies
I am also learning SwiftUI. Here is my initial solution. The button needs a stoke outline which I did not handle since I am putting the button on a green background. I am not sure if this is the best way to do it. It duplicates what I did in my Swift application so I did not spend more time on refining it. It uses buttonStyle property since I have many buttons. In my application, I named "MyButtonStyle" as "GameButtonStyle" since I am using it on a menu View for game actions. struct ExampleView: View {     var body: some View {         ZStack {             Color(red: 0, green: 0.5, blue: 0)             VStack {                 Button("Button") {                     print("tap button")                 }                 .buttonStyle(MyButtonStyle(labelWidth: 150, labelHeight: 30, scaleFactor: 1))             }         }     } } struct MyButtonStyle: ButtonStyle {     let labelWidth:CGFloat     let labelHeight:CGFloat     let scaleFactor:CGFloat     let minWidth:CGFloat     let verticalPadding:CGFloat     init(labelWidth: CGFloat, labelHeight: CGFloat, scaleFactor: CGFloat, minWidth: CGFloat = 0, verticalPadding: CGFloat = 0) {         self.labelWidth = labelWidth         self.labelHeight = labelHeight         self.scaleFactor = scaleFactor         self.minWidth = minWidth         self.verticalPadding = verticalPadding     }     func makeBody(configuration: Configuration) -> some View {         let darkBlue = Color(red: 0, green: 0, blue: 1)         configuration.label             .frame(width: labelWidth, height: labelHeight)             .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))             .font(Font.system(size: (7 * labelHeight) / 9))             .frame(minWidth: minWidth)             //.padding(.horizontal, 10)             .padding(.vertical, verticalPadding)             .foregroundColor(configuration.isPressed ? Color.white : Color.black)             .background(configuration.isPressed ? darkBlue : Color.white)             .cornerRadius(labelHeight)     } }
Post not yet marked as solved
2 Replies
In the given example, the last line is needed (hitButtonNode = nil). This could be a source of error, especially when there are many items to reset. Since GestureState automatically handles the resetting of the state, it would be nice if it wasn't reset before onEnded is started (this is my original note). I am not sure why it is being reset before .onEnded starts. My surmise is that Apple intends the gestureState to be used to change the appearance of some other item within the view (like its highlighting or its color). But, I don't see why this would require the gestureState to be reset before beginning .onEnded. This last comment leads to another comment about .onChange and .onEnded for gestures. It is not clear to me whether the mouseUp event that triggers .onEnded would first call .onChange and then call .onEnded. It would be nice if it was documented (and actually done) this way. Since I am not sure, I usually make the code in .onChange a single function and when .onEnded starts I call that function to insure that I am following the last mouse or tap event.
Post not yet marked as solved
2 Replies
I may have been misusing what GestureState was intended for. I came up with another approach that seems easier for what I want. This is what I want: I have a SpriteView with a GameScene (subclassed from SKScene). I want the SpriteView to handle the gestures (since it is way easier than the old approach). I want the tapped buttonNode in the GameScene to track the location for highlighting and unhighlighting. When the tracking ends, I want the tapped buttonNode to do its action if it is still being selected. But I cannot attach the SwiftUI gestures to the SKNode buttonNodes of the GameScene since they are not Views. Thus, I have to have a custom SwiftUI dragGesture to handle the above. The major problem is to remember the originally touched buttonNode. Instead of trying to use GestureState, I just use a locally captured variable within the "var" or "function" gesture. Here is a modified version of what I do. The modification uses force unwrapping to keep it simple. I put the dragGesture as part of the GameScene rather than as part of the file with the SpriteView. This is because of the needs for my particular program. The code can be simplified if you put it with the file with the SpriteView. For example, you can eliminate the guard statements for gameScene. The major point of the example is to how to capture the "begin" state of the gesture without using GestureState. extension GameScene {     var buttonNodeGesture: some Gesture {         var hitButtonNode:Button3Node?         return DragGesture(minimumDistance: 0)             .onChanged { [weak self] gesture in                 guard let gameScene = self else { return }                 let buttonNodes = gameScene.buttonNodes                 let firstButtonNode = buttonNodes.first!                 let parent = firstButtonNode.parent!                 do { // updating stuff                     if hitButtonNode == nil {                         let startLocation = gameScene.convert(fromGesture: gesture.startLocation, to: parent)                         hitButtonNode = gameScene.buttonNodes.filter{$0.contains(startLocation)}.first                     }                 }                 do { // onChanged stuff                     if let hitButtonNode = hitButtonNode {                         let location = gameScene.convert(fromGesture: gesture.location, to: parent)                         if hitButtonNode.contains(location) {                             hitButtonNode.highlight()                         }                         else {                             hitButtonNode.unhighlight()                         }                     }                 }             }             .onEnded { [weak self] gesture in                 guard let gameScene = self else { return }                 let buttonNodes = gameScene.buttonNodes                 let firstButtonNode = buttonNodes.first!                 let parent = firstButtonNode.parent!                 do { // onEnded stuff                     if let hitButtonNode = hitButtonNode {                         hitButtonNode.unhighlight()                         let location = gameScene.convert(fromGesture: gesture.location, to: parent)                         if hitButtonNode.contains(location) {                             hitButtonNode.doButtonAction()                         }                     }                 }                 hitButtonNode = nil // Note: probably not needed             }     } }
Post marked as solved
4 Replies
I had same problem. Here is my solution./// 190926: NodeAction is node bound with an action but waiting for a comletion argument to be given public typealias NodeAction = (_ completion: @escaping () -> Void) -> Void extension SKNode { // 190926 public func doAction(_ action:SKAction) -> NodeAction { let actionWithCompletion = { [weak self] (completion:@escaping () -> Void) in guard let this = self else { return } this.run(action, completion: completion) } return actionWithCompletion } public static func execute(nodeActions:[NodeAction]) { if let nodeAction = nodeActions.first { let rest = Array(nodeActions.dropFirst()) nodeAction { execute(nodeActions: rest) } } } }To do what you want:let arrayOfNodes:[SKNode] = [] let positionToMove = CGPoint(x: 400, y: 400) var nodeActions:[NodeAction] = [] for node in arrayOfNodes { nodeActions.append(node.doAction(SKAction.wait(forDuration: 5))) nodeActions.append(node.doAction(SKAction.move(to: positionToMove, duration: 1.0))) } SKNode.execute(nodeActions: nodeActions)I also the following additional idea which I have not tested:extension SKNode { func doActionBlock(_ block: @escaping () -> Void) -> NodeAction { // 190322 give this a try ??? let action = SKAction.run(block) let actionWithCompletion = { [weak self] (completion:@escaping () -> Void) in guard let this = self else { return } this.run(action, completion: completion) } return actionWithCompletion } }Of course, you don't have to use it just for an array. In general, you declare "var nodeActions:[NodeAction] = [ ]" and then add the actions you want to happen. Since "node" is usually just the scene, you can omit it.Here is an example where I am doing a demo of leading a card in a game:var nodeActions:[NodeAction] = [] nodeActions.append(doAction(SKAction.wait(forDuration: DemoThinker.demoThinkingDuration))) if shouldKibitz { nodeActions.append(doAction(SKAction.kibitzAction(gameEvent: gameEvent, gameScene: self))) } nodeActions.append(doAction(SKAction.demoLeadCardEventAction(for: gameEvent, gameScene: self, cardNode: cardNode))) nodeActions.append(doAction(SKAction.unhighlight(node: cardNode))) nodeActions.append(doAction(SKAction.setupDragPileNode(cardNode: cardNode, gameScene: self))) nodeActions.append(doAction(SKAction.faceup(cardNode: cardNode, faceup: true))) nodeActions.append(dragPileNode.doAction(SKAction.group([highlightWhenDiscardingAction, moveToAction]))) /// moveTo action) nodeActions.append(doAction(SKAction.wait(forDuration: didDiscardDropWaitTime))) nodeActions.append(doAction(SKAction.removeDemoBubbleNodeAction(gameScene: self))) nodeActions.append(doAction(SKAction.removeYellowDotAction(player: player, gameScene: self))) nodeActions.append(doAction(SKAction.unhighlightDiscardPileOutlineNode(player: player, gameScene: self))) nodeActions.append(doAction(SKAction.demoRestHandleLeadCardEvent(for: gameEvent, gameScene: self))) SKNode.execute(nodeActions: nodeActions)
Post marked as solved
10 Replies
It appears that there was some confusion on Apple's side. The reviewer was claiming that my iPad app was a spam of my "mac os" app of the same name. When I pointed out that I wanted the app to be both for mac os and ios, the reviewer reconsidered the app and the app was accepted.
Post marked as solved
10 Replies
I also just got rejected for "spam" for my card game Chicago Bridge IOS app. I already have a *** Rummy card game app and a Bezique card game app both accepted long ago. These games are not just variations of one another although I do try to make the interface similar. But the computer play is different for each and the implementation of the game play rules is different for each (since they are different card games).I guess I could put all three games into one bundle app and have each game be an in-app purchase. But what game would be actually purchased? Would such an app be offered for free and do nothing but allow the user to purchase one of the three apps? If I add additional card games (like spades, hearts, cribbage, etc) do I just keep adding them to this container app? Wouldn't the size of the app continue to grow (or is the code for each game downloaded on purchase rather than downloaded when the container app is purchased).