Firebase Data Returning Nil - ios

I am trying to create programmatic Radio Buttons based on dynamic Firebase data. The number of Radio Buttonsis dependent on an integer value stored in Firebase, named numberOfChildren.
The value I am receiving from Firebase is coming back nil and I cannot figure out why. Any help on how to resolve this issue so that I can return an integer value would be appreciated:
import UIKit
import FirebaseDatabase
import DLRadioButton
class PollController: UIViewController {
#IBOutlet weak var passLabel: UILabel!
#IBOutlet weak var pollImage: UIImageView!
var ref: FIRDatabaseReference!
var pollRef: FIRDatabaseReference!
var pass = ""
var passedImageURL = ""
var posX = 0;
var posY = 0;
var numberOfChildren: Int!
let label2 = UILabel(frame: CGRect(x: 90, y: 160, width: 200, height: 70))
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
pollRef = ref.child("Polls").child(pass)
passLabel.text = pass
pollImage.sd_setImage(with: URL(string: passedImageURL), placeholderImage: UIImage(named: "test"))
pollRef.observe(FIRDataEventType.value, with: {(snapshot) in
self.numberOfChildren = Int(snapshot.childSnapshot(forPath: "answers").childrenCount)
self.passLabel.text = String(self.numberOfChildren)
print(self.numberOfChildren)
})
var buttons = [DLRadioButton]()
for x in 0..<self.numberOfChildren {
let firstRadioButton = self.createRadioButton(frame: CGRect(x: CGFloat(x)*32, y: self.view.center.y, width: 40.0, height: 20.0), title: String(x), color: UIColor.green)
firstRadioButton.tag = x
buttons.append(firstRadioButton)
self.view.addSubview(firstRadioButton);
}
let groupButtons = DLRadioButton()
groupButtons.otherButtons = buttons
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func createRadioButton(frame : CGRect, title : String, color : UIColor) -> DLRadioButton {
let radioButton = DLRadioButton(frame: frame);
radioButton.titleLabel!.font = UIFont.systemFont(ofSize: 14);
radioButton.setTitle(title, for: UIControlState.normal);
radioButton.setTitleColor(color, for: UIControlState.normal);
radioButton.iconColor = color;
radioButton.indicatorColor = color;
radioButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;
radioButton.addTarget(self, action: #selector(self.logSelectedButton(_:)), for: UIControlEvents.touchUpInside);
return radioButton;
}
#objc private func logSelectedButton(_ sender: DLRadioButton){
print("Selected Button Tag = \(sender.tag) and Title \(sender.titleLabel?.text)")
}
}

The problem is in the way you nest the code. Firebase loads the data asynchronously, that's why you pass in a callback block: so that it can call your code block once the data has loaded. By the time you look over self.numChildren that data hasn't loaded yet.
The solution is to move the code that requires numChildren into the callback block.
pollRef.observe(FIRDataEventType.value, with: {(snapshot) in
self.numberOfChildren = Int(snapshot.childSnapshot(forPath: "answers").childrenCount)
self.passLabel.text = String(self.numberOfChildren)
var buttons = [DLRadioButton]()
for x in 0..<self.numberOfChildren {
let firstRadioButton = self.createRadioButton(frame: CGRect(x: CGFloat(x)*32, y: self.view.center.y, width: 40.0, height: 20.0), title: String(x), color: UIColor.green)
firstRadioButton.tag = x
buttons.append(firstRadioButton)
self.view.addSubview(firstRadioButton);
}
let groupButtons = DLRadioButton()
groupButtons.otherButtons = buttons
})
This way the loop is only invoked once numChildren has been initialized from the database.

Related

UITapGestureRecognizer doesn't work properly

I made the function updateItems() which create, from an array, many UIView's in a UIScrollView :
Here is the file where this function is :
class MainViewController: UIViewController {
#IBOutlet weak var body: UIScrollView!
#IBOutlet weak var edit: UIButton!
var _title: String = "Title"
var _isEditing: Bool = false
var firstItems: [UISectionView] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.topItem?.title = self._title
navigationController?.navigationItem.largeTitleDisplayMode = .automatic
body.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height + 100)
self.updateItems(self.firstItems)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func updateItems(_ s: [UISectionView]) {
let topMargin = 10
let rightMargin = 10
let leftMargin = 10
let space = 5
let heightItem = 60
var b = topMargin
for i in body.subviews {
i.removeFromSuperview()
}
for t in s {
if t.isHidden == true {
continue
}
if t.title != nil {
let f = UIFont(name: "Helvetica", size: 20)
let l = UILabel(frame: CGRect(x: rightMargin, y : b, width: Int(UIScreen.main.bounds.width) - (rightMargin + leftMargin), height: Int(f!.lineHeight)))
l.font = f
l.text = t.title
body.addSubview(l)
b = b + Int(f!.lineHeight) + space
}
for i in t.items{
body.addSubview(i.getView(frame: CGRect(x: rightMargin, y: b, width: Int(UIScreen.main.bounds.width) - (rightMargin + leftMargin), height: heightItem), view: self))
b = b + heightItem + space
}
}
}
}
TIPS : UISectionView is an object which contains an array of UIItemView
The object UIItemView looks like :
class UIItemView {
var icon: UIImage = UIImage();
var line1: rString = rString("")!;
var line2: rString = rString("")!;
var leftline: Any = String();
var background: String = "white"
var onItemTap: (_ sender: UITapGestureRecognizer?) -> () = {sender in }
var onItemLongPress: (_ sender: UILongPressGestureRecognizer?) -> () = {sender in }
var id: String
init?(_ id: String) {
self.id = id
}
public func getView(frame: CGRect, view: UIViewController) -> UIView {
let width = Int(frame.width)
let height = Int(frame.height)
let rightMargin = 20
let leftMargin = 10
let topMargin = 10
let bottomMargin = 10
let iconSide = height - (topMargin + bottomMargin)
let marginLine = leftMargin + iconSide + 10
let v = UIView(frame: frame)
//Background & shape
if self.background == "white" {
v.backgroundColor = UIColor.white;
} else if self.background == "blur" {
let bEV = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.extraLight))
bEV.frame = v.bounds
bEV.autoresizingMask = [.flexibleWidth, .flexibleHeight]
v.addSubview(bEV)
}
v.layer.cornerRadius = 10.0
//Icon
let i = UIImageView()
i.image = self.icon;
i.frame = CGRect(x: leftMargin, y: topMargin, width: iconSide, height: iconSide)
v.addSubview(i)
//First Line
let l1 = self.line1.getLabel()
l1.frame = CGRect(x: marginLine, y: topMargin, width: width - (marginLine + leftMargin), height: Int(self.line1.getFont().lineHeight))
v.addSubview(l1)
//Seconde Line
let l2 = self.line2.getLabel()
l2.frame = CGRect(x: marginLine, y: height - (bottomMargin + Int(self.line1.getFont().lineHeight)), width: width - (marginLine + leftMargin), height: Int(self.line1.getFont().lineHeight))
v.addSubview(l2)
//Left Line
if type(of: self.leftline) == type(of: SpinnerView()) {
let sv = (self.leftline as! SpinnerView)
sv.frame = CGRect(x: width - (rightMargin + iconSide), y: height/2 - iconSide/2, width: iconSide, height: iconSide)
v.addSubview(sv)
} else if type(of: self.leftline) == type(of: rString("")) {
let rS = (self.leftline as! rString)
if rS.text != "" {
rS.fontName = "HelveticaNeue-Bold"
rS.size = 15
rS.color = UIColor(red:0.01, green:0.48, blue:1.00, alpha:1.0)
let l3 = rS.getLabel()
l3.frame = CGRect(x: width - (rightMargin + Int(rS.getFont().lineWidth(rS.text)) + 15), y: height/2 - (Int(rS.getFont().lineHeight) + 10)/2, width: Int(rS.getFont().lineWidth(rS.text)) + 15, height: Int(rS.getFont().lineHeight) + 10)
l3.backgroundColor = UIColor(red:0.94, green:0.94, blue:0.97, alpha:1.0)
l3.layer.masksToBounds = true
l3.layer.borderWidth = 2
l3.layer.borderColor = UIColor(red:0.94, green:0.94, blue:0.97, alpha:1.0).cgColor
l3.layer.cornerRadius = rS.getFont().lineHeight/1.2
l3.textAlignment = .center
v.addSubview(l3)
}
}
//GestureRecognizer
v.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.oIT(_:))))
v.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.oILP(_:))))
v.restorationIdentifier = self.id
return v;
}
#objc func oIT(_ sender: UITapGestureRecognizer) {
print("Tap")
self.onItemTap(sender)
}
#objc func oILP(_ sender: UILongPressGestureRecognizer) {
print("LongPress")
self.onItemLongPress(sender)
}
static func ==(lhs: UIItemView, rhs: UIItemView) -> Bool {
return lhs === rhs
}
}
TIPS : UIItemView contains the function getView() which returns a specific UIView
The problem :
Everything work properly, when I load the ViewController (where there is the UIScrollView) every UIView's are build like I want, and I can interact with the UIView by the UITapGestureRecognizer or the UILongPressGestureRecognizer (the function is called as expected)
BUT
When I call the function updateItems() a second time, without reload the ViewController, the items change as expected but the UITapGestureRecognizer and the UILongPressGestureRecognizer don't work any more.
I hope you can help me :D
If information are missing for you to understand the problem, please let me know ;)

Swift Radio Buttons Not Clickable

I am using the DLRadioButton library through Cocoa Pods found here
Below is my code, essentially the radio buttons appear however I am only able to click 1 of them in the group and I am unsure why. Note that I am populating the Radio Buttons based on an Int in Firebase that indicates how many to populate:
` override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
pollRef = ref.child("Polls").child(pass)
passLabel.text = pass
pollImage.sd_setImage(with: URL(string: passedImageURL), placeholderImage: UIImage(named: "test"))
pollRef.observe(FIRDataEventType.value, with: {(snapshot) in
self.numberOfChildren = Int(snapshot.childSnapshot(forPath: "answers").childrenCount)
self.passLabel.text = String(self.numberOfChildren)
print(self.numberOfChildren)
var buttons = [DLRadioButton]()
for x in 0..<self.numberOfChildren {
let answerLabel = snapshot.childSnapshot(forPath:
"answers").childSnapshot(forPath:
String(x+1)).childSnapshot(forPath: "answer").value
let firstRadioButton = self.createRadioButton(frame: CGRect(x:
CGFloat(x)*32, y:self.view.center.y , width: 40.0, height: 20.0),
title: answerLabel as! String, color: UIColor.black)
// firstRadioButton.translatesAutore sizingMaskIntoConstraints = false
let screenSize: CGRect = UIScreen.main.bounds
firstRadioButton.frame = CGRect(x: 0, y: 0, width: 50, height: screenSize.height * 0.2)
firstRadioButton.tag = x
buttons.append(firstRadioButton)
self.view.addSubview(firstRadioButton);
}
let groupButtons = DLRadioButton()
groupButtons.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(groupButtons)
let margins = self.view.layoutMarginsGuide
groupButtons.bottomAnchor.constraint(equalTo: margins.bottomAnchor, constant: 0).isActive = true
groupButtons.otherButtons = buttons
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func createRadioButton(frame : CGRect, title : String, color : UIColor) -> DLRadioButton {
let radioButton = DLRadioButton(frame: frame);
radioButton.titleLabel!.font = UIFont.systemFont(ofSize: 14);
radioButton.setTitle(title, for: UIControlState.normal);
radioButton.setTitleColor(color, for: UIControlState.normal);
radioButton.iconColor = color;
radioButton.indicatorColor = color;
radioButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;
radioButton.addTarget(self, action: #selector(self.logSelectedButton(_:)), for: UIControlEvents.touchUpInside);
return radioButton;
}
#IBAction func logSelectedButton(_ radioButton: DLRadioButton) {
if radioButton.isMultipleSelectionEnabled {
for button: DLRadioButton in radioButton.selected() {
print("\(button.titleLabel?.text) is selected.\n")
}
}
else {
print("\(radioButton.selected()?.titleLabel?.text) is selected.\n")
}
}
`
You need to selector method of button
let firstRadioButton = self.createRadioButton(frame: CGRect(x: self.view.center.x, y: CGFloat(x)*32, width: 100.0, height: 120.0), title: answerLabel as! String, color: UIColor.green)
firstRadioButton.tag = x
firstRadioButton.addTarget(self, action: #selector(self.logSelectedButton), for: .touchUpInside)
buttons.append(firstRadioButton)
Selector method : you can change as per needs
#IBAction func logSelectedButton(_ radioButton: DLRadioButton) {
if radioButton.isMultipleSelectionEnabled {
for button: DLRadioButton in radioButton.selectedButtons {
print("\(button.titleLabel?.text) is selected.\n")
}
}
else {
print("\(radioButton.selectedButton.titleLabel?.text) is selected.\n")
}
}

Creating buttons with a for loop? Swift

func makeAButton(){
var tag = 1
for post in postArray {
let cgX = CGFloat(post.x!)
let cgY = CGFloat(post.y!)
let button : DragImg = DragImg(frame: CGRect(x: cgX, y: cgY, width: 100, height: 100))
button.backgroundColor = UIColor.blueColor()
button.setTitle(post.user, forState: .Normal)
button.addTarget(self, action: #selector(self.buttonAction), forControlEvents: .TouchUpInside)
button.tag = tag
self.view.addSubview(button)
print(tag)
tag += 1
}
}
I've currently got 7 things in my post array, and when I print out the tag, I get the
1
2
3
... and so on up to 7.
Whats happening though is that at the 0th item in the aray, 1 button is being created. Then at the 1st, 2 are being created at that x/y, and it keeps going up and up until I'm getting 7 buttons right ontop of eachother.
Why is this the case? I thought here the for loop would say:
For each post in PostArray make a single button using the data from that item in the PostArray.
How might I change this to work as intended?
Edit: (Just posting everything I've got)
import UIKit
import Firebase
class testController: UIViewController {
#IBOutlet weak var button : UIButton!
#IBOutlet weak var textField: UITextField!
var colorsArray = [String]()
var currentX = CGFloat()
var currentY = CGFloat()
var postArray = [CirclePost]()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position :CGPoint = touch.locationInView(view)
currentY = position.y
currentX = position.x
}
}
override func viewDidLoad() {
super.viewDidLoad()
DataService.ds.REF_BASE.child("Colors").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
self.postArray = []
for snap in snapshots {
if let x = snap.value!["x"] as? CGFloat, y = snap.value!["y"] as? CGFloat, color = snap.value!["color"] as? String {
let post = CirclePost(postKey: snap.key, user: color , x: x, y: y)
self.postArray.append(post)
print(self.postArray.count)
self.makeAButton()
}
}
}
}
}
func makeAButton(){
var tag = 1
for post in postArray {
let cgX = CGFloat(post.x!)
let cgY = CGFloat(post.y!)
// let button = DragImg()
let button : DragImg = DragImg(frame: CGRect(x: cgX, y: cgY, width: 100, height: 100))
button.backgroundColor = UIColor.blueColor()
button.setTitle(post.user, forState: .Normal)
button.addTarget(self, action: #selector(self.buttonAction), forControlEvents: .TouchUpInside)
button.tag = tag
self.view.addSubview(button)
tag += 1
}
}
func buttonAction(sender: UIButton!) {
let button = postArray[sender.tag - 1]
print(button.user)
print("Button tapped")
}
#IBAction func buttonPressed(sender: AnyObject) {
let ref = DataService.ds.REF_BASE.child("Colors").childByAutoId()
ref.child("color").setValue(textField.text)
ref.child("x").setValue(currentX)
ref.child("y").setValue(currentY)
}
}
Here's your issue:
for snap in snapshots {
...
self.postArray.append(post)
print(self.postArray.count)
self.makeAButton()
You're adding a post to the array, then drawing the array (1 item). Then next time you add an additional post and then draw the array (2 items) and so on.
You need to move the call self.makeAButton() out of that for loop.

Google place autocomplete in swift doesn't works perfectly

I tried to make "place autocomplete" from google place and I copy paste the codes directly from the website here google developer and I already set the api key too but the problem is why the results look so odd when I typing a place as you can see on the picture it seems works but not perfect. here is my code:
import UIKit
import GoogleMaps
class FetcherSampleViewController: UIViewController {
var textField: UITextField?
var resultText: UITextView?
var fetcher: GMSAutocompleteFetcher?
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor()
self.edgesForExtendedLayout = .None
// Set bounds to inner-west Sydney Australia.
let neBoundsCorner = CLLocationCoordinate2D(latitude: -33.843366,
longitude: 151.134002)
let swBoundsCorner = CLLocationCoordinate2D(latitude: -33.875725,
longitude: 151.200349)
let bounds = GMSCoordinateBounds(coordinate: neBoundsCorner,
coordinate: swBoundsCorner)
// Set up the autocomplete filter.
let filter = GMSAutocompleteFilter()
filter.type = .Establishment
// Create the fetcher.
fetcher = GMSAutocompleteFetcher(bounds: bounds, filter: filter)
fetcher?.delegate = self
textField = UITextField(frame: CGRect(x: 5.0, y: 0,
width: self.view.bounds.size.width - 5.0, height: 44.0))
textField?.autoresizingMask = .FlexibleWidth
textField?.addTarget(self, action: "textFieldDidChange:",
forControlEvents: .EditingChanged)
resultText = UITextView(frame: CGRect(x: 0, y: 45.0,
width: self.view.bounds.size.width,
height: self.view.bounds.size.height - 45.0))
resultText?.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
resultText?.text = "No Results"
resultText?.editable = false
self.view.addSubview(textField!)
self.view.addSubview(resultText!)
}
func textFieldDidChange(textField: UITextField) {
fetcher?.sourceTextHasChanged(textField.text!)
}
}
extension FetcherSampleViewController: GMSAutocompleteFetcherDelegate {
func didAutocompleteWithPredictions(predictions: [GMSAutocompletePrediction]) {
let resultsStr = NSMutableString()
for prediction in predictions {
resultsStr.appendFormat("%#\n", prediction.attributedPrimaryText)
}
resultText?.text = resultsStr as String
}
func didFailAutocompleteWithError(error: NSError) {
resultText?.text = error.localizedDescription
}
}
can anyone help me?
This is because prediction.attributedPrimaryText is an attributed string. Try the following code
func didAutocompleteWithPredictions(predictions: [GMSAutocompletePrediction]) {
let resultsStr = NSMutableAttributedString()
for prediction in predictions {
resultsStr.appendAttributedString(prediction.attributedPrimaryText)
resultsStr.appendAttributedString(NSAttributedString(string: "\n"))
}
resultText?.attributedText = resultsStr
}

IOS8 and Swift multiple views with gestures dont work

I'm trying to build a user interface based on generic classes which are based on the same extension of the UIViewController class. This example puts squares on the screen and the tap event would be dealt with in the parent class, but it doesnt work. What am I doing wrong?
Main ViewController.swift:
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//FIRST SQUARE
let widget1 = TestWidget()
widget1.viewX = 10
widget1.viewY = 10
self.view.addSubview(widget1.view)
self.addChildViewController(widget1)
widget1.didMoveToParentViewController(self)
//SECOND SQUARE
let widget2 = TestWidget()
widget2.viewX = 100
widget2.viewY = 100
self.view.addSubview(widget2.view)
self.addChildViewController(widget2)
widget2.didMoveToParentViewController(self)
}
}
Here is the TestWidget class:
class TestWidget : UIViewController {
var viewX : CGFloat!
var viewY : CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
let square = UIView()
square.frame = CGRect(x : viewX, y : viewY, width: 50, height: 50)
square.backgroundColor = UIColor.orangeColor()
self.view.addSubview(square)
//GESTURE PART
var tapForSquare = UITapGestureRecognizer()
tapForSquare.addTarget(self, action : "onTap")
square.addGestureRecognizer(tapForSquare)
square.userInteractionEnabled = true
square.multipleTouchEnabled = true
}
func onTap() {
println("square tapped")
}
}
I have both squares on the screen but the tap action only works on the second one. Is there any problem with this?
onTap() takes an argument. It should be:
onTap(gestureRecognizer: UITapGestureRecognizer)
and add a colon after "onTop" in this line:
tapForSquare.addTarget(self, action : "onTap")
Thanks everyone, it seems interestingly this works only if you set the parent views frame as well.
So something like:
override func viewDidLoad() {
super.viewDidLoad()
self.view.frame = CGRect(x : viewX, y : viewY, width: 50, height: 50)
let square = UIView()
square.frame = CGRect(x : 0, y : 0, width: 50, height: 50)
square.backgroundColor = UIColor.orangeColor()
self.view.addSubview(square)
...
}

Resources