Reload BarButtonItem after pressing (Change formatted string title) - swift

I have added a bar button item programmatically that will change the temperature units on some variables between Celsius and Fahrenheit and have set it with attributed text via a customView.
It is half Bold and half standard text. Whichever unit is selected will be bold.
After trying each method of refreshing them I would remove them after they didn't work. So in the code below there is no "reload/refresh" being attempted.
I have a boolean which changes when the action is called. However I cannot get the barButtomItem to "Reload/refresh" upon being selected.
I have debugged and the action is being called, the variable is changing, and the formatted text is being changed, however it is not changing on the screen.
//MARK: - Create Navigation Bar
func createNavBar() {
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.backgroundColor = .clear
navigationController?.navigationBar.isTranslucent = true
let CFLabel = UILabel()
CFLabel.attributedText = formatCFButton()
CFLabel.sizeToFit()
CFLabel.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(CFChanger))
CFLabel.addGestureRecognizer(tap)
let CFButton = UIBarButtonItem.init(customView: CFLabel)
let addCityButton = UIBarButtonItem.init(barButtonSystemItem: .add, target: self, action: #selector(addCity))
addCityButton.tintColor = .black
self.navigationItem.rightBarButtonItem = addCityButton
self.navigationItem.leftBarButtonItem = CFButton
}
#objc func CFChanger() {
print("CF BUTTON PRESSED AND FUNCTION ACTIVATED!")
isCelsius = !isCelsius
}
//Change font(bold) change temperature units button
func formatCFButton() -> NSMutableAttributedString {
let changeTempString = NSMutableAttributedString()
let boldAttribute = [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Bold", size: 18.0)!]
let regularAttribute = [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Light", size: 18.0)!]
if isCelsius == true {
let boldText = NSAttributedString(string: "C ", attributes: boldAttribute)
let regularText = NSAttributedString(string: "/ F", attributes: regularAttribute)
changeTempString.append(boldText)
changeTempString.append(regularText)
} else {
let boldText = NSAttributedString(string: "F ", attributes: boldAttribute)
let regularText = NSAttributedString(string: "C / ", attributes: regularAttribute)
changeTempString.append(regularText)
changeTempString.append(boldText)
}
return changeTempString
}
I was able to get this to work in the past when I did NOT create the button programmatically by adding this code into the function when the button is pressed. I am not sure how to do this programmatically. (sender stuff)
// MARK: - Change units function
//****************************************************************************
#IBAction func changeUnitsButtonPressed(_ sender: UIButton) {
isCelsius = !isCelsius
var tempUnitNumber = 0
if isCelsius == true {
tempUnitNumber = 1
} else {
tempUnitNumber = 2
}
defaults.set(tempUnitNumber, forKey: "tempUnit")
let changeTempString = formatCFButton()
sender.setAttributedTitle(changeTempString, for: [])
tableView.reloadData()
}

Related

User profile information is not showing up using swift and firestore database

pic of database
I am trying to show a label with the user's status on their profile page. after logging in the user gets presented with a VC that has a side menu. on that side menu is a "profile" option. once choosing this they go to their profile controller. right now i simply need to search users/current uid/ "MembershipStatus" and present this result into a label called "welcomeLabel".
I am returning nul
import UIKit
import Firebase
class NonMemberProfileController: UIViewController {
// MARK: - Properties
var welcomeLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = UIFont.systemFont(ofSize: 28)
label.translatesAutoresizingMaskIntoConstraints = false
label.alpha = 0
return label
}()
// MARK: - Init
override func viewDidLoad()
{
super.viewDidLoad()
authenticateUserAndConfigureView()
}
func loadUserData()
{
guard let uid = Auth.auth().currentUser?.uid else {return}
//.child("MembershipStatus")
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value) {
(snapshot) in
if snapshot.hasChild("MembershipStatus"){
print("true we have bingo")
} else {
print("no bueno")
dump(snapshot)
}
guard let status = snapshot.value as? String else { return }
self.welcomeLabel.text = "Welcome, \(status)"
print("this is lkjfdskjklsfad" + status)
UIView.animate(withDuration: 0.5, animations: {
self.welcomeLabel.alpha = 1
})
}
}
func authenticateUserAndConfigureView(){
if Auth.auth().currentUser == nil {
DispatchQueue.main.async {
let navController = UINavigationController(rootViewController: LoginViewController())
navController.navigationBar.barStyle = .black
self.present(navController, animated: true, completion: nil)
}
} else {
configureNavigationBar()
loadUserData()
}
}
// MARK: - Selectors
#objc func handleDismiss() {
dismiss(animated: true, completion: nil)
}
// MARK: - Helper Functions
func configureNavigationBar() {
view.backgroundColor = UIColor.lightGray
navigationItem.title = "Profile"
navigationController?.navigationBar.barTintColor = .darkGray
navigationController?.navigationBar.barStyle = .black
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Home_2x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "baseline_settings_white_24dp").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
view.addSubview(welcomeLabel)
welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
welcomeLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
You are using Cloud Firestore for data storage but your code is reading data form RealTime Database. You have to read data like this:
let userRef = Firestore.firestore().collection("users").document(uid)
userRef.getDocument { (documentSnapshot, error) in guard
let document = documentSnapshot?.data() else {
print(error)
return
}
print(document)
}

UIButton not highlighting or running code when clicked

I'm setting up 26 buttons, adding them as subviews to a letterButtonLabel, and appending them to an letterButtons array.
for row in 0..<6 {
for column in 0..<5 {
if row == 5 && column > 0 { continue }
let letterButton = UIButton(type: .system)
letterButton.titleLabel?.font = UIFont.systemFont(ofSize: 36)
letterButton.setTitle("O", for: .normal)
letterButton.addTarget(self, action: #selector(letterTapped), for: .touchUpInside)
let frame = CGRect(x: width * column, y: height * row, width: width, height: height)
letterButton.frame = frame
letterButtonLabel.addSubview(letterButton)
letterButtons.append(letterButton)
}
}
Afterwards, I assign a letter to each button using the letterButtons array.
var index = 0
for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
letterButtons[index].setTitle("\(letter)", for: .normal)
index += 1
}
I then manually add layout constraints.
Here is the functionality for each button:
#objc func letterTapped(_ sender: UIButton) {
// check the letter and react
sender.isHidden = true
guard let word = currentWord else { return }
guard var charactersArray = currentWordField.text?.components(separatedBy: "") else { return }
guard let originalCurrentWordField = currentWordField.text else { return }
for (index, letter) in word.enumerated() {
let strLetter = String(letter)
if strLetter == sender.titleLabel?.text {
charactersArray[index] = strLetter
}
}
currentWordField.text = charactersArray.joined()
if currentWordField.text == originalCurrentWordField {
livesLeft -= 1
}
if wordBank.contains(word) {
// you got it! reset letters
level += 1
let ac = UIAlertController(title: "Good job!", message: nil, preferredStyle: .alert)
let action = UIAlertAction(title: "Continue", style: .default, handler: resetForNextLevel)
ac.addAction(action)
present(ac, animated: true)
}
if livesLeft == 0 {
let ac = UIAlertController(title: "Game Over", message: nil, preferredStyle: .alert)
let action = UIAlertAction(title: "Restart", style: .default, handler: resetForNextLevel)
ac.addAction(action)
present(ac, animated: true)
}
}
(adding all just in case)
My button doesn't highlight or click. Is there anything here that might explain the problem? I'm curious about common mistakes.
UILabel has by default userInteractionEnabled = false.
Setting this to true should fix your problem

Create a Login Page for SKSprite Game

I am in the process of creating a game (Swift) in xcode using a number of SKScene and Sprite objects. I want to create a Scene (settings scene) that captures the player's name, email, gender etc. How can I go about this? How can I capture input from user. SKScenes do not allow input fields/values in the UI?
Thanks
You can build a custom login page that is conform with your game layout without try to rebuild in UIKit the same graphic assets.
Few days ago I've written an answer about SKSceneDelegate to communicate between the scene(SpriteKit) and the viewController (UIKit), take present this answer if you want to call other viewControllers because its the same concept of this answer..
Starting with this GameViewController we can develop some useful methods to handle the login form buttons and show some alerts:
import UIKit
import SpriteKit
class GameViewController: UIViewController, TransitionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .fill
scene.delegate = self as TransitionDelegate
scene.anchorPoint = CGPoint.zero
view.presentScene(scene)
}
func showAlert(title:String,message:String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default) { action in
print("handle Ok action...")
})
alertController.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
self.present(alertController, animated: true)
}
func handleLoginBtn(username:String,password:String) {
print("handleLoginBtn")
print("username is: \(username) and password: \(password)")
}
func handleFacebookBtn() {
print("handleFacebookBtn")
}
func handleTwitterBtn() {
print("handleTwitterBtn")
}
}
Then we can make our scene trying to take the advantage of SpriteKit elements:
import SpriteKit
import UIKit
protocol TransitionDelegate: SKSceneDelegate {
func showAlert(title:String,message:String)
func handleLoginBtn(username:String,password:String)
func handleFacebookBtn()
func handleTwitterBtn()
}
class GameScene: SKScene,UITextFieldDelegate {
var usernameTextField:UITextField!
var passwordTextField:UITextField!
var loginBtn:SKShapeNode!
var facebookBtn:SKShapeNode!
var twitterBtn:SKShapeNode!
override func didMove(to view: SKView) {
//bg
let bg = SKSpriteNode(imageNamed: "appleWallpaper")
addChild(bg)
bg.position = CGPoint(x:self.size.width/2,y:self.size.height/2)
//title
let title = SKLabelNode.init(fontNamed: "AppleSDGothicNeo-Bold")
title.text = "xyzGame"; title.fontSize = 25
title.fontColor = .orange
addChild(title)
title.zPosition = 1
title.position = CGPoint(x:self.size.width/2,y:self.size.height-80)
//textfields
guard let view = self.view else { return }
let originX = (view.frame.size.width - view.frame.size.width/1.5)/2
usernameTextField = UITextField(frame: CGRect.init(x: originX, y: view.frame.size.height/4.5, width: view.frame.size.width/1.5, height: 30))
customize(textField: usernameTextField, placeholder: "Enter your username")
view.addSubview(usernameTextField)
usernameTextField.addTarget(self, action:#selector(GameScene.textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
passwordTextField = UITextField(frame: CGRect.init(x: originX, y: view.frame.size.height/4.5+60, width: view.frame.size.width/1.5, height: 30))
customize(textField: passwordTextField, placeholder: "Enter your password", isSecureTextEntry:true)
view.addSubview(passwordTextField)
//buttons
let myBlue = SKColor(colorLiteralRed: 59/255, green: 89/255, blue: 153/255, alpha: 1)
loginBtn = getButton(frame: CGRect(x:self.size.width/4,y:self.size.height/2,width:self.size.width/2,height:30),fillColor:myBlue,title:"Login",logo:nil,name:"loginBtn")
addChild(loginBtn)
loginBtn.zPosition = 1
let label = SKLabelNode.init(fontNamed: "AppleSDGothicNeo-Regular")
label.text = "or connect with"; label.fontSize = 15
label.fontColor = .gray
addChild(label)
label.zPosition = 1
label.position = CGPoint(x:self.size.width/2,y:self.size.height/2-30)
let logoFb = SKSpriteNode.init(imageNamed: "facebook-icon")
logoFb.setScale(0.5)
facebookBtn = getButton(frame: CGRect(x:self.size.width/4,y:self.size.height/2-80,width:self.size.width/4.5,height:30),fillColor:myBlue,logo:logoFb,name:"facebookBtn")
addChild(facebookBtn)
facebookBtn.zPosition = 1
let myCyan = SKColor(colorLiteralRed: 85/255, green: 172/255, blue: 239/255, alpha: 1)
let logoTw = SKSpriteNode.init(imageNamed: "twitter-icon")
logoTw.setScale(0.5)
twitterBtn = getButton(frame: CGRect(x:self.size.width/2,y:self.size.height/2-80,width:self.size.width/4.5,height:30),fillColor:myCyan,logo:logoTw,name:"twitterBtn")
addChild(twitterBtn)
twitterBtn.zPosition = 1
}
func customize(textField:UITextField, placeholder:String , isSecureTextEntry:Bool = false) {
let paddingView = UIView(frame:CGRect(x:0,y: 0,width: 10,height: 30))
textField.leftView = paddingView
textField.keyboardType = UIKeyboardType.emailAddress
textField.leftViewMode = UITextFieldViewMode.always
textField.attributedPlaceholder = NSAttributedString(string: placeholder,attributes: [NSForegroundColorAttributeName: UIColor.gray])
textField.autocapitalizationType = .none
textField.autocorrectionType = .no
textField.layer.borderColor = UIColor.gray.cgColor
textField.layer.borderWidth = 0.5
textField.layer.cornerRadius = 4.0
textField.textColor = .white
textField.isSecureTextEntry = isSecureTextEntry
textField.delegate = self
}
func getButton(frame:CGRect,fillColor:SKColor,title:String = "",logo:SKSpriteNode!,name:String)->SKShapeNode {
let btn = SKShapeNode(rect: frame, cornerRadius: 10)
btn.fillColor = fillColor
btn.strokeColor = fillColor
if let l = logo {
btn.addChild(l)
l.zPosition = 2
l.position = CGPoint(x:frame.origin.x+(frame.size.width/2),y:frame.origin.y+(frame.size.height/2))
l.name = name
}
if !title.isEmpty {
let label = SKLabelNode.init(fontNamed: "AppleSDGothicNeo-Regular")
label.text = title; label.fontSize = 15
label.fontColor = .white
btn.addChild(label)
label.zPosition = 3
label.position = CGPoint(x:frame.origin.x+(frame.size.width/2),y:frame.origin.y+(frame.size.height/4))
label.name = name
}
btn.name = name
return btn
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
switch name {
case "loginBtn":
self.run(SKAction.wait(forDuration: 0.1),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).handleLoginBtn(username:self.usernameTextField.text!,password: self.passwordTextField.text!)
})
case "facebookBtn":
self.run(SKAction.wait(forDuration: 0.1),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).handleFacebookBtn()
})
case "twitterBtn":
self.run(SKAction.wait(forDuration: 0.1),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).handleTwitterBtn()
})
default:break
}
}
}
func textFieldDidChange(textField: UITextField) {
//print("everytime you type something this is fired..")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == usernameTextField { // validate email syntax
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
let result = emailTest.evaluate(with: textField.text)
let title = "Alert title"
let message = result ? "This is a correct email" : "Wrong email syntax"
if !result {
self.run(SKAction.wait(forDuration: 0.01),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).showAlert(title:title,message: message)
})
}
}
}
deinit {
print("\n THE SCENE \((type(of: self))) WAS REMOVED FROM MEMORY (DEINIT) \n")
}
}
Output:
Animated output:
As you can see we can handle both framework with their delegate methods, I've tested this page with iPhone 5 and iPhone 7 plus.

iMessage app won't insert message into active conversation

I'm having an issue with a Message Extension app I'm trying to create. Whenever I try to insert text into a conversation nothing happens. This is what I'm using:
import UIKit
import Messages
class MessagesViewController: MSMessagesAppViewController {
override func viewDidLoad() {
super.viewDidLoad()
let sendButton = UIButton(type: UIButtonType.custom)
sendButton.frame = CGRect(x: view.frame.midX, y: view.frame.midY, width: 100, height: 50)
sendButton.setTitle("Send", for: UIControlState.normal)
sendButton.addTarget(self, action: #selector(sendButtonTapped(sender:)), for: UIControlEvents.touchUpInside)
self.view.addSubview(sendButton)
}
func sendButtonTapped(sender: UIButton!){
let layout = MSMessageTemplateLayout()
layout.caption = "My Survey"
layout.image = UIImage(named: "myImage")
let message = MSMessage()
message.layout = layout
self.activeConversation?.insert(message, completionHandler: nil)
}
}
Whenever I run it using this code nothing happens. The weird part is that if I change self.activeConversation? to self.activeConversation! the app crashes. I don't understand why this is happening because I'm in a conversation and my app has loaded.
Can you try this.
func sendButtonTapped(sender: UIButton!)
{
guard let conversation = activeConversation else { fatalError("Expected a conversation") }
let session = conversation.selectedMessage?.session ?? MSSession()
let message = MSMessage(session: session)
let layout = MSMessageTemplateLayout()
layout.image = createImageForMessage(value: GlobalData.sharedManager.cardUrl)
layout.caption = "Caption"
layout.imageTitle = GlobalData.sharedManager.cardTitle
layout.imageSubtitle = "Image subtitle"
layout.trailingCaption = "Trailing caption"
layout.subcaption = "Subcaption"
layout.trailingSubcaption = "Trailing subcaption"
message.layout = layout
conversation.insert(message) {error in
// empty for now
}
}}

swift UIDatePicker change font, font color, font size etc

I've been trying to change font in native UIDatePicker in iOS and I did it, but with some unsettled details:
I use extension for UIDatePicker to change font in it's labels:
extension UIDatePicker {
func stylizeView(view: UIView? = nil) {
let view = view ?? self
for subview in view.subviews {
if let label = subview as? UILabel {
if let text = label.text {
print("UIDatePicker :: sylizeLabel :: \(text)\n")
label.font = UIFont(name: "MyriadPro-Light", size: 17)!
}
} else { stylizeView(subview) }
}
}}
So, you can customize font deeply:
struct DatePickerStyle {
let tintColor = UIColor(hex: 0xFFFFFF)
let font = UIFont(name: "MyriadPro-Light", size: 17)!
let fontColor = UIColor(hex: 0x000000)
let fontKern: CGFloat = 0.2
var paragraphStyle: NSMutableParagraphStyle {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 4
paragraphStyle.lineHeightMultiple = 1
paragraphStyle.alignment = .Right
return paragraphStyle
}}
extension UIDatePicker {
func stylizeView(view: UIView? = nil) {
let style = DatePickerStyle()
let view = view ?? self
for subview in view.subviews {
if let label = subview as? UILabel {
if let text = label.text {
print("UIDatePicker :: sylizeLabel :: \(text)\n")
let attributedString = NSMutableAttributedString(string: text)
let attributedStringRange = NSMakeRange(0, attributedString.length)
attributedString.addAttributes([
NSParagraphStyleAttributeName: style.paragraphStyle,
NSFontAttributeName: style.font,
NSForegroundColorAttributeName: style.fontColor,
NSKernAttributeName: style.fontKern
], range: attributedStringRange)
//label.font = style.font
label.tintColor = style.fontColor
label.attributedText = attributedString
}
} else { stylizeView(subview) }
}
}
}
This function in extension is implemented on any Control Events of UIDatePicker:
datePicker.addTarget(self, action: #selector(CellWithDatePicker.updateDatePickerStyle), forControlEvents: .AllEvents)
&
func updateDatePickerStyle() {
print(":: updateDatePickerStyle")
datePicker.stylizeView()
}
Problem 1:
When I init UIDatePicker, font of the picker is still SanFrancisco.
But when I change value in UIDatePicker the font is changed to my font
My Font
I tried to implement datePicker.stylizeView() or self.stylizeView() on every stage of UIDatePicker lifecycle, but it can only change selected line of DatePicker.
Problem 2:
While I rotating DatePicker after pic.2 when all label of DatePicker is set with newFont, new labels which is outside the selected line is still with old font (SanFrancisco). And when I stop rotating DatePicker all label is updated to newFont.
enter image description here
Any idea how to fix it?
Use GCD with an interval of 0.1 to call the styling function.
Example:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1)
{
self.datePicker.stylizeView();ss
}
The only way for changing the font of UIDatePickerView (until now) is swizzling:
you can change the font by an extension of UILabel! (this is not recommended but it works!)
import Foundation
import UIKit
public extension UILabel {
#objc func setFontSwizzled(font: UIFont) {
if self.shouldOverride() {
self.setFontSwizzled(font: UIFont.fontWith(style: .regular, size: 14))
} else {
self.setFontSwizzled(font: font)
}
}
private func shouldOverride() -> Bool {
let classes = ["UIDatePicker", "UIDatePickerWeekMonthDayView", "UIDatePickerContentView"]
var view = self.superview
while view != nil {
let className = NSStringFromClass(type(of: view!))
if classes.contains(className) {
return true
}
view = view!.superview
}
return false
}
private static let swizzledSetFontImplementation: Void = {
let instance: UILabel = UILabel()
let aClass: AnyClass! = object_getClass(instance)
let originalMethod = class_getInstanceMethod(aClass, #selector(setter: font))
let swizzledMethod = class_getInstanceMethod(aClass, #selector(setFontSwizzled))
if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
// switch implementation..
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}()
static func swizzleSetFont() {
_ = self.swizzledSetFontImplementation
}
}
and for changing the color you just simply call the function below:
datePicker.setValue(UIColor.whiteColor(), forKeyPath: "textColor")
if it's necessary to be re-rendered you need to call:
datePicker.datePickerMode = .CountDownTimer
datePicker.datePickerMode = .DateAndTime //or whatever your original mode was