I'm an iOS and programming noob so I apologize for any bad phrasing or mistakes.
I'm parsing quotes from an API for my app which displays it on a textLabel each time a UIButton is clicked. In order to keep the string from going off the textLabel or be resized to an unreadable font, I'm trying to request a new quote if the string character count is too high by calling a function in my NSObject. I set up a NSObject to do the refetching but whenever I try to reassign the the string to the textLabel.text from the NSObject or try to send the string back to the ViewController the qouteLabel.text comes back nil
Here is my viewcontroller where I'm making the initial request for the quote
import UIKit
import Alamofire
class RSQuotesViewController: RSViewController {
var ronImageView: UIImageView!
var quoteLabel = UILabel!()
override func loadView() {
let frame = UIScreen.mainScreen().bounds
let view = UIView(frame: frame)
view.backgroundColor = UIColor.grayColor()
ronImageView = UIImageView(frame: CGRectMake(frame.width/2-160, frame.height-600, 320, 600))
let ron = "ron.png"
let ronImage = UIImage(named: ron)
ronImageView.image = ronImage
view.addSubview(ronImageView);
let labelWidth = ronImageView.frame.width/2
let quoteLabelX = labelWidth-40
quoteLabel = UILabel(frame: CGRect(x: quoteLabelX, y: ronImageView.frame.height/4+15, width: labelWidth, height: 160))
quoteLabel.textAlignment = .Center
quoteLabel.text = "Click to Start"
quoteLabel.shadowColor = UIColor.grayColor()
quoteLabel.adjustsFontSizeToFitWidth = true
quoteLabel.lineBreakMode = .ByWordWrapping // or NSLineBreakMode.ByWordWrapping
quoteLabel.numberOfLines = 0
view.addSubview(quoteLabel)
self.view = view
}
override func viewDidLoad() {
super.viewDidLoad()
let frame = UIScreen.mainScreen().bounds
let getQuote = UIButton(frame: CGRect(x: 0, y: 0, width: frame.size.width+50, height: frame.size.height))
getQuote.backgroundColor = UIColor.clearColor()
getQuote.setTitle("", forState: UIControlState.Normal)
getQuote.addTarget(self, action: #selector(RSQuotesViewController.getQuote(_:)), forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(getQuote)
}
// Gets quote when button is pressed
func getQuote(sender: UIButton){
let url = "http://ron-swanson-quotes.herokuapp.com/v2/quotes"
Alamofire.request(.GET, url, parameters: nil).responseJSON { response in
if let JSON = response.result.value as? Array<String>{
let quoteDict = RSQoute()
// if quote is too large get another one
if (JSON[0].characters.count > 120){
print("greater than 120")
quoteDict.fetchQuote()
} else {
self.quoteLabel.text = JSON[0]
}
}
}
}
This is my model where I'm trying to reassign the quoteLabel.text and getting nil
import UIKit
import Alamofire
class RSQoute: NSObject {
var newQuote = String()
// fetchs new quote if quote is too large
func fetchQuote(){
let url = "http://ron-swanson-quotes.herokuapp.com/v2/quotes"
Alamofire.request(.GET, url, parameters: nil).responseJSON { response in
if let JSON = response.result.value as? Array<String>{
self.newQuote = JSON[0]
if (self.newQuote.characters.count > 120) {
print("Try Again: ---->\(self.newQuote)")
return self.fetchQuote()
} else {
let quoteVC = RSQuotesViewController()
print("Retry was less than 120: ---->\(self.newQuote)")
print("quoteLabelText: ---->\(RSQuotesViewController().quoteLabel.text)")// comes back nil
RSQuotesViewController().quoteLabel.text = self.newQuote
}
}
}
}
}
Please let me know if there something I'm missing or an easier/better way of trying to fetch a new quote from the API :)
In your function fetchQuote()
, you set quoteVC as a new instantiation of RSQuotesViewController() with let quoteVC = RSQuotesViewController()
. Instead you should be setting the quoteLabel.text
for the applications instance of RSQuotesViewController()
. You are also making two API requests. Once inside the fetchQuote()
function for RSQuotesViewController
and once inside your fetchQuote()
function for RSQuotes
I think what you are looking for would involve closures. Try this out for your fetchQuote()
function in your RSQuotes
class
func fetchQuote(completion: (result:String)){
let url = "http://ron-swanson-quotes.herokuapp.com/v2/quotes"
Alamofire.request(.GET, url, parameters: nil).responseJSON { response in
if let JSON = response.result.value as? Array<String>{
self.newQuote = JSON[0]
if (self.newQuote.characters.count > 120) {
print("Try Again: ---->\(self.newQuote)")
completion(result: self.newQuote)
} else {
print("Retry was less than 120: ---->\(self.newQuote)")
print("quoteLabelText: ---->\(RSQuotesViewController().quoteLabel.text)")// comes back nil
completion(result: self.newQuote)
}
}
Then, I would have a setQuote
function RSQuotesViewController
where you could just do something like this
func setQuote() {
let quoteObj = RSQuote()
quoteObj.fetchQuote() {
result in
quoteLabel.text = result
}
}
I would take a look at some posts related to swift closures and also check out. http://goshdarnclosuresyntax.com/
On a side note, I'm not sure if you were planning to manipulate the quoteString within your RSQuote
class. If not, it might be better for fetchQuote()
to be a static func
. This way you can just call it without initializing the object in RSQuoteViewController
. It'd be something like RSQuote.fetchQuote()