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)
}
Related
Hello everyone!)
Need some help!)
I have custom text field with input limit which was in my view controller. If you look below, you will see that my text field has: UIView (underlayer with some borders), two UILabels (name label and counter label), and UITextField inside of UIView. Now I want to make UITextField subclass and configure my text field there with whole UI-es.
MARK: - I working without storyboards, the code only.
The question is, can I implement this in UITextField class?) Or maybe better to use UIView class?)
I experimented and tried to do it in TextField class, but stuck on UIView (underlayer), I can't make it behind my text field. I add a bit of code.)
Have you any ideas how to implement this in right way?)
Thanks for every answer!)
Example
Code...
UIViewController class
import UIKit
class ViewController: UIViewController {
var inputLimitTextField = InputLimitTextField(frame: CGRect(x: 45, y: 200, width: 300, height: 40))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(inputLimitTextField)
}
}
UITextField class
import UIKit
class InputLimitTextField: UITextField {
var underlayerView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
configureTextField()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configureTextField()
}
func configureTextField() {
backgroundColor = .purple
underlayerView.backgroundColor = .red
underlayerView.alpha = 0.5
addSubview(underlayerView)
underlayerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
underlayerView.topAnchor.constraint(equalTo: self.bottomAnchor),
underlayerView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
underlayerView.centerXAnchor.constraint(equalTo: self.centerXAnchor)
])
}
override func layoutSubviews() {
super.layoutSubviews()
underlayerView.frame = self.bounds
sendSubviewToBack(underlayerView)
}
}
Considering the fact that there is still no answer to my question that would solve this issue… Also, given that using subclasses is a pretty popular practice in programming... I didn't find a specific answer to such a question on the stack. That's why I decided to answer my own question. I hope my approach to solving the problem helps someone in the future...
Code...
UIViewController class...
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
private lazy var inputLimitTextField = InputLimitTextField()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(inputLimitTextField)
inputLimitTextFieldPosition()
}
private func inputLimitTextFieldPosition() {
inputLimitTextField.center.x = self.view.center.x
inputLimitTextField.center.y = self.view.center.y - 100
}
}
UITextField class...
import UIKit
class InputLimitTextField: UITextField, UITextFieldDelegate {
private lazy var nameLabel = UILabel()
private lazy var counterLabel = UILabel()
private let textLayer = CATextLayer()
private let padding = UIEdgeInsets(top: 0.5, left: 10, bottom: 0.5, right: 17)
private let purpleUIColor = UIColor(red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1.0)
private let purpleCGColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(),
components: [0.2849253164, 0.1806431101, 0.5, 1.0])
private let redUIColor = UIColor(red: 1, green: 0.1806431101, blue: 0.09760022642, alpha: 1)
private let redCGColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(),
components: [ 1, 0.1806431101, 0.09760022642, 1.0])
override init(frame: CGRect) {
super.init(frame: frame)
configureTextField()
configureNameLabel()
configureCunterLabel()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configureTextField()
configureNameLabel()
configureCunterLabel()
}
private func configureTextField() {
let screenRect = UIScreen.main.bounds
let screenWidth = screenRect.size.width - 25
let textFieldFrame = CGRect(x: 0, y: 0, width: screenWidth, height: 40)
frame = textFieldFrame
backgroundColor = .clear
textColor = purpleUIColor
font = UIFont(name: "Helvetica", size: 17)
placeholder = "Input limit"
textAlignment = .left
contentVerticalAlignment = .center
clearButtonMode = .always
autocorrectionType = .no
keyboardType = .default
returnKeyType = .done
delegate = self
textLayer.backgroundColor = UIColor.white.cgColor
textLayer.borderColor = purpleCGColor
textLayer.borderWidth = 1.2
textLayer.cornerRadius = 10
textLayer.frame = layer.bounds
layer.insertSublayer(textLayer, at: 0)
layer.shadowColor = .init(gray: 0.5, alpha: 0.5)
layer.shadowOpacity = 0.7
layer.shadowOffset = .init(width: 2, height: 2)
addSubview(nameLabel)
nameLabel.frame = CGRect(x: 12, y: -12, width: 55, height: 16)
addSubview(counterLabel)
counterLabel.frame = CGRect(x: screenWidth - 34, y: 9, width: 22, height: 22)
}
override internal func textRect(forBounds bounds: CGRect) -> CGRect {
let bounds = super.textRect(forBounds: bounds)
return bounds.inset(by: padding)
}
override internal func editingRect(forBounds bounds: CGRect) -> CGRect {
let bounds = super.editingRect(forBounds: bounds)
return bounds.inset(by: padding)
}
override internal func clearButtonRect(forBounds bounds: CGRect) -> CGRect {
let screenRect = UIScreen.main.bounds
let screenWidth = screenRect.size.width - 25
return CGRect(x: screenWidth - 70, y: 0, width: 40, height: 40)
}
private func enableUI() {
self.textLayer.borderColor = redCGColor
self.counterLabel.layer.borderColor = redCGColor
self.counterLabel.textColor = redUIColor
self.textColor = redUIColor
self.nameLabel.layer.borderColor = redCGColor
self.nameLabel.textColor = redUIColor
}
private func disableUI() {
self.textLayer.borderColor = purpleCGColor
self.counterLabel.layer.borderColor = purpleCGColor
self.counterLabel.textColor = purpleUIColor
self.textColor = purpleUIColor
self.nameLabel.layer.borderColor = purpleCGColor
self.nameLabel.textColor = purpleUIColor
}
func firstTenCharsColor(text: String) -> NSMutableAttributedString {
let characterCount = 10
let stringLength = text.utf16.count
let attributedString = NSMutableAttributedString(string: text)
if stringLength >= characterCount {
attributedString.addAttribute(.foregroundColor, value: #colorLiteral( red: 0.2849253164, green: 0.1806431101, blue: 0.5, alpha: 1), range: NSMakeRange(0, characterCount) )
}
return attributedString
}
private func updateUI(inputText: String?) {
guard let textCount = inputText?.count else { return }
guard let text = self.text else { return }
if (textCount <= 10){
self.counterLabel.text = "\(10 - textCount)"
disableUI()
} else if (textCount >= 10) {
self.counterLabel.text = "\(10 - textCount)"
enableUI()
self.attributedText = firstTenCharsColor(text: text)
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = self.text, let textRange = Range(range, in: text) else { return true }
let updatedText = text.replacingCharacters(in: textRange, with: string)
self.updateUI(inputText: updatedText)
return true
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
self.textLayer.borderColor = purpleCGColor
self.counterLabel.text = "10"
disableUI()
return true
}
private func configureNameLabel() {
nameLabel.backgroundColor = .white
nameLabel.layer.cornerRadius = 3
nameLabel.layer.borderWidth = 1.2
nameLabel.layer.borderColor = purpleCGColor
nameLabel.layer.masksToBounds = true
nameLabel.font = UIFont(name: "Helvetica", size: 11)
nameLabel.text = "Input limit"
nameLabel.textAlignment = .center
nameLabel.textColor = purpleUIColor
}
private func configureCunterLabel() {
counterLabel.backgroundColor = .white
counterLabel.layer.cornerRadius = 5
counterLabel.layer.borderWidth = 1.2
counterLabel.layer.borderColor = purpleCGColor
counterLabel.layer.masksToBounds = true
counterLabel.font = UIFont(name: "Helvetica", size: 12)
counterLabel.text = "10"
counterLabel.textAlignment = .center
counterLabel.textColor = purpleUIColor
}
}
You can use it for any iPhone...
Stay safe and good luck! :)
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()
}
I have a subclassed UILabel that I am going to be using for about 10 different labels of varying length (all on 1 line!) in IB. The text within each of these labels are static.
This subclassed UILabel should display an imageView to the left of the text. This imageView will hold a small icon.
It's close to working, except I'm getting odd truncating & alignment as seen from the screenshot below.
Simulator:
Storyboard setup:
Here's my code:
class ImageMarkLabel: UILabel {
var labelHeight:CGFloat = 0.0
let smileView = UIView()
let smileImageView = UIImageView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
uiStuff()
}
func uiStuff() {
labelHeight = self.frame.height
smileView.frame = CGRect(x: 0, y: 0, width: labelHeight, height: labelHeight)
smileImageView.image = UIImage(named: "smile")
smileImageView.frame = CGRect(x: 0, y: 0, width: smileView.frame.width, height: smileView.frame.height)
}
func commonInit(){
smileView.addSubview(smileImageView)
self.addSubview(smileView)
}
override func drawText(in rect: CGRect) {
var insets = UIEdgeInsets()
if UIDevice.current.userInterfaceIdiom == .pad {
insets = UIEdgeInsets(top: 0, left: labelHeight + 10.0, bottom: 0, right: labelHeight + 10.0)
}
else {
insets = UIEdgeInsets(top: 0, left: labelHeight + 5.0, bottom: 0, right: labelHeight + 5.0)
}
super.drawText(in: rect.inset(by: insets))
}
}
Regarding Auto Layout - all of the labels are within a vertical stack view.
How can I get the text & imageView to be centered without causing any text truncating?
Change you frame in UIStuff method ,
class ImageMarkLabel: UILabel {
var labelHeight:CGFloat = 0.0
let smileView = UIView()
let smileImageView = UIImageView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
uiStuff()
}
func uiStuff() {
labelHeight = self.frame.height
smileView.frame = CGRect(x: 0, y: 0, width: labelHeight, height: labelHeight)
smileImageView.image = UIImage(named: "smile")
smileImageView.frame = CGRect(x: 0, y: 0, width: smileView.frame.width, height: smileView.frame.height)
var insets = UIEdgeInsets()
if UIDevice.current.userInterfaceIdiom == .pad {
insets = UIEdgeInsets(top: 0, left: (labelHeight + 10.0), bottom: 0, right: (labelHeight + 10.0))
}
else {
insets = UIEdgeInsets(top: 0, left: (labelHeight + 5.0), bottom: 0, right: (labelHeight + 5.0))
}
let newRect = CGRect(origin: self.frame.origin, size: CGSize(width: self.frame.width + 2*insets.left, height: self.frame.height))
self.frame = newRect
}
func commonInit(){
smileView.addSubview(smileImageView)
self.addSubview(smileView)
}
override func drawText(in rect: CGRect) {
super.drawText(in: rect)
}
}
Output:
I want to design a button respecting these rules:
Content horizontally centered
18px margin on both side of the content
10px between image and title
Title will wrap on multiple line if needed
Use attributed title
I have a working solution using:
button.setTitle(title, for: .normal)
Result:
But I can't figure why it's not working if I use:
button.setAttributedTitle(NSAttributedString(string: title), for: .normal)
Result:
It does not respect the 10px and 18px insets. Also note that the problem only occurs when the title becomes multiline.
Here is the playground I used to pinpoint my problem:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
extension UIImage {
class func imageWithView(view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
class ActionBarView: UIView {
// MARK: Properties
private var icon: UIImage?
private let stackView = UIStackView()
private let proposeButton = UIButton()
private let declineButton = UIButton()
// MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
designView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: Design
private func designView() {
designIcon()
designStackView()
designProposeButton()
designDeclineButton()
}
private func designIcon() {
let image = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
image.backgroundColor = .magenta
icon = UIImage.imageWithView(view: image)
}
private func designStackView() {
stackView.addArrangedSubview(proposeButton)
stackView.addArrangedSubview(declineButton)
stackView.distribution = .fillEqually
stackView.spacing = 1
addSubview(stackView)
stackView.frame = frame
}
private func designProposeButton() {
design(button: proposeButton,
icon: icon,
title: "Propose a task")
}
private func designDeclineButton() {
design(button: declineButton,
icon: icon,
title: "Decline")
}
private func design(button: UIButton, icon: UIImage?, title: String) {
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
button.titleLabel?.backgroundColor = .green
button.imageView?.backgroundColor = .orange
button.backgroundColor = .white
button.setImage(icon, for: .normal)
// button.setAttributedTitle(NSAttributedString(string: title), for: .normal)
button.setTitle(title, for: .normal)
button.titleLabel?.lineBreakMode = .byWordWrapping
}
}
let view = ActionBarView(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
PlaygroundPage.current.liveView = view
So I'm trying to create a Playground that functions as a 'presentation', meaning that I have multiple slides and it's interactive-ish. I'm new to Swift, so would really use some help.
This is my main code
import UIKit
import SpriteKit
import PlaygroundSupport
let frame = CGRect(x: 0, y: 0, width: 640, height: 480)
let view = SKView(frame: frame)
view.showsDrawCount = true
view.showsNodeCount = true
view.showsFPS = true
let scene = MainScene(size: CGSize(width: 640, height: 480))
view.presentScene(scene) //error here, explained below
PlaygroundPage.current.liveView = view
This is my MainScene.swift code -
import UIKit
import SpriteKit
import PlaygroundSupport
public class Slide: SKView {
public var title: SKLabelNode!
public var info: UITextView!
public func setter() {
self.title = SKLabelNode(fontNamed: "Chalkduster")
self.title.verticalAlignmentMode = .center
self.title.color = SKColor.white
self.title.position = CGPoint(x: frame.midX, y: 440)
let framer = CGRect(x: 40, y: 60, width: 560, height: 360)
self.info = UITextView(frame: framer)
self.info.font = UIFont(name: "Chalkduster", size: 20)
self.info.textAlignment = .center
self.info.textColor = UIColor.white
self.info.backgroundColor = UIColor.black
}
}
public class MainScene: SKScene {
public var button1 = SKSpriteNode(imageNamed: "Next")
public var button2 = SKSpriteNode(imageNamed: "Previous")
public var scene1: Slide?
public override func didMove(to view: SKView) {
backgroundColor = SKColor.black
scene1?.setter()
setScene1()
}
public func setScene1() {
scene1?.title = SKLabelNode(fontNamed: "Chalkduster")
scene1?.title.text = "Title."
scene1?.title.position = CGPoint(x: frame.midX, y: frame.midY)
scene1?.info.text = "Text."
addChild(scene1?.title)
addButtons()
}
public func addButtons() {
self.button1.position = CGPoint(x: 600, y: 40)
self.button2.position = CGPoint(x: 40, y: 40)
addChild(self.button1)
addChild(self.button2)
}
}
Basically, I was tying to setup multiple slides through functions that would change the previous slide's title/info. However, I thought it would be much better to have an SKView class defined for my slides in order to use SKTransition.
However, with my current code, I'm running into an execution interrupted error. Also, when I change stuff around, the error kind of followed me into either Expected Declaration or no Class Initializers for MainScene.swift.
Really, I just want to fix up my declaration of the class Slide and then use that class to make multiple slides.
Here's an example without SpriteKit. You should really review some basic Swift and UIKit lessons to get a good foundation on the language
import UIKit
import PlaygroundSupport
public class Slide: UIView {
public var title: UILabel
public var info: UITextView
required public override init(frame:CGRect) {
self.title = UILabel()
self.title.font = UIFont(name: "Chalkduster", size: 20)
self.title.textColor = UIColor.white
self.title.backgroundColor = UIColor.red
self.title.center = CGPoint(x: frame.midX, y: 440)
self.info = UITextView()
self.info.font = UIFont(name: "Chalkduster", size: 20)
self.info.textAlignment = .center
self.info.textColor = UIColor.white
self.info.backgroundColor = UIColor.blue
super.init(frame: frame)
addSubview(title)
addSubview(info)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
public class MainScene: UIView {
public var scene1: Slide
required public override init(frame: CGRect) {
scene1 = Slide()
super.init(frame: frame)
scene1.title.text = "Title."
scene1.title.sizeToFit()
scene1.title.center = CGPoint(x: frame.midX, y: frame.midY - 20)
scene1.info.text = "Text."
scene1.info.sizeToFit()
scene1.info.center = CGPoint(x: frame.midX, y: frame.midY + scene1.title.frame.height + 20)
addSubview(scene1)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let frame = CGRect(x: 0, y: 0, width: 640, height: 480)
let view = UIView(frame: frame)
let scene = MainScene(frame: frame)
view.addSubview(scene)
PlaygroundPage.current.liveView = view