Thanks, Claude31, but nope. This does not address the issue. Exactly the same behavior. Cheers.
Post
Replies
Boosts
Views
Activity
Hello.I think, I know why I had issues with my code. Looks like the textViewDidChange method was also influenced by didChangeSize(oldSize:) method which was also called every time I typed in a character and which I needed to handle rotation of my device.Anyways, I think I have come up with a solution how to handle expanding a textView which also gets adjusted with a rotation. Here is the code for the peer review.import UIKit
class ViewController: UIViewController, UITextViewDelegate
{
var textView: UITextView!
var btn: UIButton!
var btnSend: UIButton!
var keyboardHeight : CGFloat = 0
var isKeyboardShown = false
var textViewHeight : CGFloat = 0
var textView_inset : CGFloat = 10
var textView_insetBottom : CGFloat = 5
let btnSendWidth : CGFloat = 50
let btnSendHeight : CGFloat = 50
override func viewDidLoad()
{
super.viewDidLoad()
textViewHeight = 35
textView = UITextView(frame: CGRect(x: textView_inset+self.view.safeAreaInsets.left,
y: self.view!.bounds.height,
width: self.view.frame.width-textView_inset*3-self.view.safeAreaInsets.left-self.view.safeAreaInsets.right-btnSendWidth,
height: textViewHeight))
textView.textAlignment = NSTextAlignment.justified
textView.backgroundColor = UIColor.lightGray
// Use RGB colour
textView.backgroundColor = UIColor(red: 39/255, green: 53/255, blue: 182/255, alpha: 1)
// Update UITextView font size and colour
textView.font = UIFont(name: "Verdana", size: 17)
textView.textColor = UIColor.white
// Make UITextView web links clickable
textView.isSelectable = true
textView.isEditable = true
textView.dataDetectorTypes = UIDataDetectorTypes.link
textView.isScrollEnabled = false
// Make UITextView corners rounded
textView.layer.cornerRadius = 10
// Enable auto-correction and Spellcheck
textView.autocorrectionType = UITextAutocorrectionType.yes
textView.spellCheckingType = UITextSpellCheckingType.yes
// myTextView.autocapitalizationType = UITextAutocapitalizationType.None
self.view.addSubview(textView)
textView.delegate = self
registerForKeyboardNotifications()
btn = UIButton()
btn.setTitle("PRESS", for: .normal)
btn.setTitleColor(.red, for: .normal)
btn.frame = CGRect(x: 50, y: 150, width: 100, height: 50)
btn.addTarget(self, action: #selector(self.showTextView(sender:)), for: .touchUpInside)
self.view.addSubview(btn)
btnSend = UIButton()
btnSend.setTitle("SEND", for: .normal)
btnSend.setTitleColor(.white, for: .normal)
btnSend.backgroundColor = .red
btnSend.frame = CGRect(x: self.view.frame.width-self.view.safeAreaInsets.right-btnSendWidth-textView_inset,
y: self.view!.bounds.height,
width: btnSendWidth,
height: btnSendHeight)
btnSend.addTarget(self, action: #selector(self.sendMessage(sender:)), for: .touchUpInside)
self.view.addSubview(btnSend)
}
override func viewWillLayoutSubviews()
{
var textView_y : CGFloat = 0
var btnSend_y : CGFloat = 0
// Required to ensure that the TextView's height is changed dynamically!
if isKeyboardShown
{
textView_y = self.view!.bounds.height-keyboardHeight-textView.frame.height-textView_insetBottom
btnSend_y = self.view!.bounds.height-keyboardHeight-btnSendHeight-textView_insetBottom
}
else
{
textView_y = self.view!.bounds.height
btnSend_y = self.view!.bounds.height
}
textView.frame = CGRect(x: textView_inset+self.view.safeAreaInsets.left,
y: textView_y,
width: self.view.frame.width-textView_inset*3-self.view.safeAreaInsets.left-self.view.safeAreaInsets.right-btnSendWidth,
height: textViewHeight)
btnSend.frame = CGRect(x: self.view.frame.width-self.view.safeAreaInsets.right-self.btnSendWidth-textView_inset,
y: btnSend_y,
width: btnSendWidth,
height: btnSendHeight)
}
@objc func showTextView(sender: UIButton!)
{
textView.becomeFirstResponder()
}
@objc func sendMessage(sender: UIButton!)
{
print("------SEND MESSAGE-------")
}
func textViewDidChange(_ textView: UITextView)
{
let fixedWidth = textView.frame.size.width
// Changing height of the message UITextView
var newSize = textView.sizeThatFits(CGSize.init(width: fixedWidth, height: CGFloat(MAXFLOAT)))
// Limit the height to 100
if newSize.height > 100
{
newSize.height = 100
textView.isScrollEnabled = true
}
else
{
textView.isScrollEnabled = false
}
var newFrame = textView.frame
newFrame.size = CGSize.init(width: CGFloat(fmaxf(Float(newSize.width), Float(fixedWidth))), height: newSize.height)
textViewHeight = newFrame.height
var newOrigin = textView.frame.origin
newOrigin.y -= (newSize.height - newFrame.size.height)
textView.frame = CGRect(origin: textView.frame.origin, size: CGSize(width: newFrame.width, height: newFrame.height))
}
func registerForKeyboardNotifications()
{
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver( self,
selector: #selector(ViewController.keyboardWillShow(_:)),
name: UIResponder.keyboardWillShowNotification,
object: nil )
notificationCenter.addObserver( self,
selector: #selector(ViewController.keyboardWillBeHidden(_:)),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
func unregisterForKeyboardNotifications()
{
let notificationCenter = NotificationCenter.default
notificationCenter.removeObserver( self,
name: UIResponder.keyboardWillShowNotification,
object: nil)
notificationCenter.removeObserver( self,
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
@objc func keyboardWillShow(_ notification: Notification)
{
isKeyboardShown = true
// Reset the position of the textview to the bottom of the screen
textView.frame = CGRect(x: textView_inset+self.view.safeAreaInsets.left,
y: self.view!.bounds.height,
width: self.view.frame.width-textView_inset*3-self.view.safeAreaInsets.left-self.view.safeAreaInsets.right-btnSendWidth,
height: textViewHeight)
btnSend.frame = CGRect(x: self.view.frame.width-self.view.safeAreaInsets.right-btnSendWidth-textView_inset,
y: self.view!.bounds.height,
width: btnSendWidth,
height: btnSendHeight)
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
{
keyboardHeight = keyboardFrame.cgRectValue.height
// Place the TextView above the keyboard
self.textView.frame.origin.y -= (keyboardHeight+self.textView.frame.height+textView_insetBottom)
// Determine a new TextView height dynamically
let fixedWidth = textView.frame.size.width // Width never changes
var newSize = textView.sizeThatFits(CGSize.init(width: fixedWidth, height: CGFloat(MAXFLOAT))) // Height is based on the number of characters
// Limit the height to 100
if newSize.height > 100
{
newSize.height = 100
textView.isScrollEnabled = true
}
else
{
textView.isScrollEnabled = false
}
var newFrame = textView.frame // A new frame: Fixed width; Dynamically changed height
newFrame.size = CGSize.init(width: CGFloat(fmaxf(Float(newSize.width), Float(fixedWidth))), height: newSize.height)
textViewHeight = newFrame.height // Assign the height value to the object property
textView.frame = CGRect(origin: textView.frame.origin, size: CGSize(width: newFrame.width, height: newFrame.height))
self.textView.layoutIfNeeded()
self.btnSend.frame.origin.y -= (keyboardHeight+self.btnSend.frame.height+textView_insetBottom)
btnSend.frame = CGRect(origin: btnSend.frame.origin, size: CGSize(width: btnSendWidth, height: btnSendHeight))
}
}
@objc func keyboardWillBeHidden(_ notification : NSNotification)
{
isKeyboardShown = false
if let _: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
{
//let newHeight: CGFloat
let duration:TimeInterval = (notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
keyboardHeight = 0
UIView.animate(withDuration: duration,
delay: TimeInterval(0),
options: animationCurve,
animations: {
self.textView.frame.origin.y = self.view!.bounds.height
self.textView.layoutIfNeeded()
self.btnSend.frame.origin.y = self.view!.bounds.height
},
completion: nil)
}
}
func textViewDidBeginEditing(_ textView: UITextView)
{
textView.becomeFirstResponder()
let tapRecognizer = UITapGestureRecognizer(target: self,
action: #selector(tapDetected(_:)))
if self.view != nil {self.view!.addGestureRecognizer(tapRecognizer)}
}
func textViewDidEndEditing(_ textView: UITextView)
{
// Make the active field nil to hide the keyboard
//self.activeField = nil;
}
@objc func tapDetected(_ tapRecognizer: UITapGestureRecognizer)
{
textView?.resignFirstResponder()
if self.view != nil {self.view!.removeGestureRecognizer(tapRecognizer)}
}
}Thanks!
Good observation, Claude31.Replaced with:textView.frame = CGRect(origin: newOrigin, size: CGSize(width: newFrame.width, height: newFrame.height))If you notice any other issues, this would be greatly appreciated!Thanks a lot!Igor
iniitamo.Thank you for the response.Is it possible to use SKLabelNode to enter text a-la UITextField?I have read a lot of negative stuff about SKShapeNodes. Thought, many issues had already been solved. I am trying to use rectangulars with rounded corners. How would I do that with SKSpriteNode? Just create a rounded png image and use it as a node texture? The main problem with this approach occurs when my node size changes, e.g. when the height is much smaller than width, the texture image gets disproprtionally squeezed/stretched. Something I do not have with SKShapeNode.Cheers!
At the end, I abandoned this approach. Takes so much memory to the extent that my daughter's old iPad would crash with an out of memory warning...
What do you mean by "iterated"? If I understand your subject correctly... This can be used when node 1 is just partially within the boundary of the second node:if node1.intersects(node2) { }If you want to know, whether your first node is entirely within the boundaries of the second node, this should work:// Node 1 intersects HEIGHT of Node2
if ( node1BottomY <= node2TopY && node1TopY >= node2BottomY ) || ( node1TopY >= node2BottomY && node1BottomY <= node2TopY )
{
intersectsHeight = true
}
// Node 1 intersects WIDTH of Node 2
if ( node1LeftX <=node2RightX && node1RightX >= node2LeftX ) || ( node1RightX >= node2LeftX && node1LeftX <= node2RightX )
{
intersectsWidth = true
}
// If Node1 intersects both height and width of Node 2, it is completely withoin its boundaries:
if intersectsHeight == true && intersectsWidth == true { }This is how you may check if half of the first node intesects the second node:if node2.contains(CGPoint(x: node1.frame.midX, y: node1.frame.midY))
Thanks, bg2b. Interesting approach. Is there more information on how to use a custom shader to blur the background that you could recommend?Cheers.
if !node2.intersects(node1)
Finally, I have figured it out. In Settings -> iTunes&App Stores, the Sandbox Account included my normal email address. This also had to be changed to the Sandbox account.
The problem has not been solved in Xcode 12.1.
Please disregard. I cannot find an option to delete the post.
Hi. I ran into the same problem. Did you happen to find the solution? Thanks!
You can authenticate by using a method along the following lines (in the GameCenterHelp that will be a singleton):
func authenticatePlayer() {
GKLocalPlayer.local.authenticateHandler = { gcAuthVC, error in
				// In your delegate, implement method `didChangeAuthStatus`, for example to enable a Game Center button
				self.delegate?.didChangeAuthStatus(isAuthenticated: GKLocalPlayer.local.isAuthenticated)
				// This is called if the local player gets authenticated. Nothing happens, GKLocalPlayer gets registered
				if GKLocalPlayer.local.isAuthenticated {
GKLocalPlayer.local.register(self)
}
// This is called, when a player opens your app and he|she is not authenticated.
				 // It opens a viewController with sign-in info
				else if let vc = gcAuthVC {
self.delegate?.presentGameCenterAuth(viewController: vc)
}
				// Handing an error while authenticating
				else {
print(">>>>> Error authenticating the Player! \(error?.localizedDescription ?? "none") <<<<<")
				}
}
So, in your GameScene class you may have something like:
override func didMove(to view: SKView) {
		 GameCenterHelper.sharedInstance.delegate = self
GameCenterManager.sharedInstance.authenticatePlayer()
}
@objc func didChangeAuthStatus(isAuthenticated: Bool) {
... For example, stop spinning activity indicator
	if indicator != nil {
indicator!.stop()
indicator!.removeFromSuperview()
indicator = nil
}
... Or enable the *PLAY ON GAMECENTER* button
if GKLocalPlayer.local.isAuthenticated {
btnPlayGameCenter.setButtonLabel(title: btnPlayGameCenterTitle(), font: "Noteworthy-Bold", fontSize: 40)
}
		
		/** Present the view controller with the Sign-in info */
		func presentGameCenterAuth(viewController: UIViewController?) {
guard let vc = viewController else {return}
self.view!.window?.rootViewController?.present(vc, animated: true)
}
That should work with any kind of app. It is only a matter where you call authenticatePlayer() and assign the delegate: in didMove(view:) or viewDidLoad() or whatever SwiftUI initial method is (I am old school).
No worries. Please feel free to upvote my response if it works. Cheers.