UITextField not showing when added as subview in SpriteKit - swift

I'm pretty new to Swift and I'm having some trouble displaying UITextField in SpriteKit. Init() in SceneMenu will not display the UITextField, while using function showTextField() with touchesBegan() works. How can I display UITextField without the user clicking the button Show TextField?
GameViewController.swift:
class GameViewController: UIViewController {
override func viewDidLoad() {
let scene = SceneMenu(size: view.frame.size)
scene.scaleMode = .aspectFill
scene.backgroundColor = .white
let view = view as! SKView
view.presentScene(scene)
}
}
SceneMenu.swift:
class SceneMenu: SKScene {
override init(size: CGSize) {
super.init(size: size)
let btnAlert = SKLabelNode(text: "Show TextField")
btnAlert.name = "btn_text"
btnAlert.fontSize = 20
btnAlert.fontColor = SKColor.blue
btnAlert.fontName = "Avenir"
btnAlert.position = CGPoint(x: size.width / 2, y: size.height / 2)
addChild(btnAlert)
let textFieldFrame = CGRect(origin: CGPoint(x: 100, y: 300), size: CGSize(width: 200, height: 30))
let textField = UITextField(frame: textFieldFrame)
textField.backgroundColor = SKColor.blue
textField.placeholder = "Type name"
view?.addSubview(textField)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showTextField() {
let textFieldFrame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 30))
let textField = UITextField(frame: textFieldFrame)
textField.backgroundColor = SKColor.blue
textField.placeholder = "Type name"
view?.addSubview(textField)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let action = atPoint(location)
if action.name == "btn_text" {
showTextField()
}
}
}
}

SKScene has a method that lets you setup the scene when you move to it. So, instead of using the custom class's initializer, override and use SKScene's didMove(to view: SKView) method. An example:
override func didMove(to view: SKView) {
showTextField()
}

Related

Draggable UIView does not move smoothly and is only draggable for a little distance (Video included)

The video: https://www.youtube.com/watch?v=8_xCTJAld9Y&feature=youtu.be
. Here you can see my issue, and I do not know what exactly is wrong. I cant move it like I want.
I have a draggable UIView, which has a custom class:
import UIKit
class UIViewDraggable: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.isUserInteractionEnabled = true
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first;
let location = touch?.location(in: self.superview);
if(location != nil)
{
self.frame.origin = CGPoint(x: location!.x-self.frame.size.width/2, y: location!.y-self.frame.size.height/2);
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
I create the UIView with a button tap:
#IBAction func buttonAddSubviewTextTapped(_ sender: UIButton) {
let label = UIView()
label.frame = CGRect(x: 0, y: 0, width: 200, height: 21)
label.center = CGPoint(x: 160, y: 285)
label.backgroundColor = .red
self.viewImagePreview.addSubview(label)
self.viewImagePreview.bringSubviewToFront(label)
}
How can I implement a fully smoothly draggable UIView?
Final Solution to make button created view draggable within its parentview:
#IBAction func buttonAddSubviewTextTapped(_ sender: UIButton) {
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
let label = UIView()
label.frame = CGRect(x: 0, y: 0, width: 200, height: 21)
label.center = CGPoint(x: 160, y: 285)
label.backgroundColor = .red
label.isUserInteractionEnabled = true
label.addGestureRecognizer(panGesture)
self.viewImagePreview.addSubview(label)
self.viewImagePreview.bringSubviewToFront(label)
}
// Function to drag a view
#objc func draggedView(_ sender: UIPanGestureRecognizer) {
guard let createdView = sender.view else {return}
createdView.bringSubviewToFront(viewImagePreview)
if sender.state == .began || sender.state == .changed {
let point = sender.location(in: self.viewImagePreview)
if let parentView = self.viewImagePreview {
let superBounds = CGRect(x: parentView.bounds.origin.x, y: parentView.bounds.origin.y, width: parentView.bounds.size.width - 2, height: parentView.bounds.size.height - 2)
if (superBounds.contains(point)) {
let translation = sender.translation(in: parentView)
createdView.center = CGPoint(x: createdView.center.x + translation.x, y: createdView.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: parentView)
}
}
}
}
Add a panGesture to your View and then based on the touch location, updated your view.center
let pan = UIPanGestureRecognizer(target: self, action: #selector(panView))
self.addGestureRecognizer(pan)
then use it as:
#objc func panView(pan: UIPanGestureRecognizer) {
let location = pan.location(in: self.superView) // get pan location
switch pan.state {
case .began:
print("began")
case .changed:
self.center = location
// updated your frames here
default:
print("nothing")
}
}
Also for your case,
try replacing
self.frame.origin = CGPoint(x: location!.x-self.frame.size.width/2, y: location!.y-self.frame.size.height/2);
to
self.center = location

How to cut a specific part from my CALayer

I have a UIView. My UIView has a layer. I added another layer to it. I need to cut a circle in this layer when the user moves his finger to create a drawing effect. I will leave my code, but it is not correct, as it creates a circle once.
class CustomImageView: UIImageView {
private var recognizer: UIPanGestureRecognizer!
private var maskLayer: CALayer!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
private func setup() {
self.isUserInteractionEnabled = true
recognizer = UIPanGestureRecognizer(target: self, action: #selector(swipeGesture))
self.addGestureRecognizer(recognizer)
maskLayer = CALayer()
maskLayer.backgroundColor = UIColor.black.withAlphaComponent(0.6).cgColor
maskLayer.frame = self.bounds
self.layer.insertSublayer(maskLayer, at: 0)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func crateMask(location: CGPoint, layer: CALayer) {
let maskFrame = CGRect(origin: CGPoint(x: location.x - 25 , y: location.y - 25 ), size: CGSize(width: 50, height: 50))
layer.createMask(maskRect: maskFrame, invert: true)
}
#objc func swipeGesture(sender: UIPanGestureRecognizer) {
let location = sender.location(in: self)
print(location)
crateMask(location: location, layer: self.maskLayer )
}
}
extension CALayer {
func createMask(maskRect: CGRect, invert: Bool = false) {
print(maskRect)
let maskLayer = CAShapeLayer()
let path = CGMutablePath()
if (invert) {
path.addRect(self.bounds)
}
let cornerRadius = maskRect.height / 2
path.addRoundedRect(in: maskRect, cornerWidth: cornerRadius, cornerHeight: cornerRadius)
maskLayer.path = path
if (invert) {
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
}
self.mask = maskLayer
}
}
Here is the screen shot of the UI: https://prnt.sc/ozki6s
Any help will be appreciated! Thanks in advance.

Enable user interaction on overlay view

I'm creating an darken overlay on top of my UITableView, I would like to highlight one of the row. I'm able to highlight it, but how can I enable user interaction (e.g. tap on the cell)?
func addFullScreenDarkOverlay(){
let viewDarkOverlay = UIView()
viewDarkOverlay.alpha = 0.5
viewDarkOverlay.backgroundColor = .black
viewDarkOverlay.frame = UIApplication.shared.keyWindow!.frame
// add dummy hole (will change it to the frame of the particular cell)
let holeFrame = CGRect(x: 0, y: 0, width: viewDarkOverlay.frame.width/2, height: viewDarkOverlay.frame.height/2)
// Do any additional setup after loading the view.
let path = CGMutablePath()
// Dark background
path.addRect(CGRect(x: 0, y: 0, width: viewDarkOverlay.frame.width, height: viewDarkOverlay.frame.height))
// White/spotlight path
path.addRect(holeFrame)
let maskLayer = CAShapeLayer()
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path;
maskLayer.fillRule = kCAFillRuleEvenOdd
viewDarkOverlay.layer.mask = maskLayer
viewDarkOverlay.clipsToBounds = true
UIApplication.shared.keyWindow!.addSubview(viewDarkOverlay)
}
Basically, you can disable the user interaction of the mask view, that will pass the all the touch events to the view below... however, if you just want the highlighted area to be intractable (i.e. the cell), you can create a custom UIView subclass, override the hitTest method, like so:
import UIKit
class MaskView: UIView {
var hitableArea: CGRect {
return CGRect(x: 0, y: 0, width: bounds.width * 0.5, height: bounds.height * 0.5)
}
override init(frame: CGRect) {
super.init(frame: frame)
installMaskLayer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
installMaskLayer()
}
private func installMaskLayer() {
backgroundColor = UIColor.black.withAlphaComponent(0.5)
let path = CGMutablePath()
// Dark background
path.addRect(bounds)
// White/spotlight path
path.addRect(hitableArea)
let maskLayer = CAShapeLayer()
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path;
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
layer.mask = maskLayer
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if (hitableArea.contains(point)) {
return nil
}
return self
}
}
Just create this custom view and add it to your window, and now you can only touch the cell now.
Remove the user interaction in your overlay view and you will be able to select the rows in the view below it:
viewDarkOverlay.isUserInteractionEnabled = false
If you want the selection only inside the whole you can transfer the touch from the overlay view to your parent view using a delegate like so:
protocol OverlaySelection: class{
func selected(with touch: UITouch?)
}
class OverlayView: UIView{
weak var delegate : OverlaySelection?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
delegate?.selected(with: touches.first)
}
}
then in your parent view:
func addFullScreenDarkOverlay(){
let viewDarkOverlay = OverlayView()
viewDarkOverlay.delegate = self
//other config..
}
//Delegate
func selected(with touch: UITouch?) {
if let location = touch?.location(in: self.view){
//Here you check if this point is inside the whole and if it is you select
//the row, if not just return
let indexpath = tableview.indexPathForRow(at: location)
tableview.selectRow(at: indexpath, animated: true, scrollPosition: .bottom)
}
}

UITextField is not visible on SKScene

I try to add a UITextField to a SKScene. I'm working with Swift 3 and Xcode 8. I tried the following:
import Foundation
import SpriteKit
class MyScene: SKScene {
init(size: CGSize, score: Highscore, optionen: GameOptions) {
super.init(size: size)
let stepsTextField = UITextField(frame: CGRect(x: 20, y: 100, width: 300, height: 40))
stepsTextField.placeholder = "Enter text here"
stepsTextField.font = UIFont.systemFont(ofSize: 15)
stepsTextField.borderStyle = UITextBorderStyle.roundedRect
stepsTextField.autocorrectionType = UITextAutocorrectionType.no
stepsTextField.keyboardType = UIKeyboardType.default
stepsTextField.returnKeyType = UIReturnKeyType.done
stepsTextField.clearButtonMode = UITextFieldViewMode.whileEditing;
stepsTextField.contentVerticalAlignment = UIControlContentVerticalAlignment.center
self.view?.addSubview(stepsTextField)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
But all I get is a black screen. What did I miss?
Thanks for your help!
You need to add it into didMove(to view: SKView)
By adding your code to the following function, you can see your text field.
override func didMove(to view: SKView) {
let stepsTextField = UITextField(frame: CGRect(x: 20, y: 100, width: 300, height: 40))
stepsTextField.placeholder = "Enter text here"
stepsTextField.font = UIFont.systemFont(ofSize: 15)
stepsTextField.borderStyle = UITextBorderStyle.roundedRect
stepsTextField.autocorrectionType = UITextAutocorrectionType.no
stepsTextField.keyboardType = UIKeyboardType.default
stepsTextField.returnKeyType = UIReturnKeyType.done
stepsTextField.clearButtonMode = UITextFieldViewMode.whileEditing;
stepsTextField.contentVerticalAlignment = UIControlContentVerticalAlignment.center
self.view?.addSubview(stepsTextField)
}

Present Scene with Sprite Kit in Swift

So when i start my game the you can play until you die, then the main menu Scene is presented and if you touch on the screen in the main menu you should load the GameScene again but i get an Error, i use this code to present the GameScene:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let revealGameScene = SKTransition.fade(withDuration: 0.5)
let goToGameScene = GameScene(size: self.size)
goToGameScene.scaleMode = SKSceneScaleMode.aspectFill
self.view?.presentScene(goToGameScene, transition:revealGameScene)
}
and the Same to present the main menu when you die which works perfectly fine but for some reason it doesn't work when i try to present the GameScene from the main menu
I think that there has to be an error outside of what you're showing us. I can create a very basic project that starts on a MenuScene (directed there by the GameViewController), when you click anywhere on the MenuScene it takes you to the GameScene. In the GameScene if you click a green box it takes you back to the MenuScene. And round and round you go.
Take a look at the code and see if anything different from yours
class GameViewController: UIViewController {
var menuScene: MenuScene!
var skView: SKView!
override func viewDidLoad() {
super.viewDidLoad()
// Create and configure the scene.
skView = self.view as! SKView
menuScene = MenuScene(size: skView.bounds.size)
menuScene.scaleMode = .aspectFill
skView.ignoresSiblingOrder = true
skView.showsFPS = true
skView.showsNodeCount = true
// Present the scene.
skView.presentScene(menuScene)
}
}
class MenuScene: SKScene {
override init(size: CGSize) {
super.init(size: size)
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let background = SKSpriteNode(color: .red, size: self.size)
background.position = CGPoint(x: 0, y: 0)
addChild(background)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let revealGameScene = SKTransition.fade(withDuration: 0.5)
let goToGameScene = GameScene(size: self.size)
goToGameScene.scaleMode = SKSceneScaleMode.aspectFill
self.view?.presentScene(goToGameScene, transition:revealGameScene)
}
}
class GameScene: SKScene {
private var box = SKSpriteNode()
override func didMove(to view: SKView) {
box = SKSpriteNode(color: .green, size: CGSize(width: 200, height: 200))
box.position = CGPoint.zero
addChild(box)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: self)
if box.frame.contains(touchLocation) {
let revealGameScene = SKTransition.fade(withDuration: 0.5)
let goToMenuScene = MenuScene(size: self.size)
goToMenuScene.scaleMode = SKSceneScaleMode.aspectFill
self.view?.presentScene(goToMenuScene, transition:revealGameScene)
}
}
}
}