How to call a function inside a function in Swift 3 - function

I try to call a function tapBlarButton(_ sender: UITapGestureRecognizer) in viewWillAppear function but I don't know how to do it, this is my code:
#IBOutlet weak var buttonFunction: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapBlarButton(_:)))
buttonFunction.addGestureRecognizer(tapGesture)
}
func tapBlarButton(_ sender: UITapGestureRecognizer) {
print("Hello! 😘")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Here I want to call the function
}

Related

swift When I click on didselectRow I want to see the json data on the detail page image , title and year?

When I click on it, I want to show the picture title and year part in the cell section in the tableview, how can I do this? When we click on the row go detail page and big picture, title and year I want to show the big title and year. How can I populate the DetailViewController page?
ViewController
import UIKit
class ViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView!
#IBOutlet var field: UITextField!
var movies = [Movie]()
override func viewDidLoad() {
super.viewDidLoad()
table.register(MovieTableViewCell.nib(), forCellReuseIdentifier: MovieTableViewCell.identifier)
table.delegate = self
table.dataSource = self
field.delegate = self
}
// Field
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchMovies()
return true
}
func searchMovies() {
field.resignFirstResponder()
guard let text = field.text, !text.isEmpty else {
return
}
let query = text.replacingOccurrences(of: " ", with: "%20")
movies.removeAll()
URLSession.shared.dataTask(with: URL(string: "https://www.omdbapi.com/?apikey=3aea79ac&s=\(query)&type=movie")!,
completionHandler: { data, response, error in
guard let data = data, error == nil else {
return
}
// Convert
var result: MovieResult?
do {
result = try JSONDecoder().decode(MovieResult.self, from: data)
}
catch {
print("error")
}
guard let finalResult = result else {
return
}
// Update our movies array
let newMovies = finalResult.Search
self.movies.append(contentsOf: newMovies)
// Refresh our table
DispatchQueue.main.async {
self.table.reloadData()
}
}).resume()
}
// Table
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieTableViewCell.identifier, for: indexPath) as! MovieTableViewCell
cell.configure(with: movies[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Show movie details
if let vc = storyboard?.instantiateViewController(identifier: "detailViewController") as? detailViewController{
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}
struct MovieResult: Codable {
let Search: [Movie]
}
struct Movie: Codable {
let Title: String
let Year: String
let imdbID: String
let _Type: String
let Poster: String
private enum CodingKeys: String, CodingKey {
case Title, Year, imdbID, _Type = "Type", Poster
}
}
MovieTableViewCell
class MovieTableViewCell: UITableViewCell {
#IBOutlet var movieTitleLabel: UILabel!
#IBOutlet var movieYearLabel: UILabel!
#IBOutlet var moviePosterImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
static let identifier = "MovieTableViewCell"
static func nib() -> UINib {
return UINib(nibName: "MovieTableViewCell",
bundle: nil)
}
func configure(with model: Movie) {
self.movieTitleLabel.text = model.Title
self.movieYearLabel.text = model.Year
let url = model.Poster
if let data = try? Data(contentsOf: URL(string: url)!) {
self.moviePosterImageView.image = UIImage(data: data)
}
}
}
DetailViewController
import UIKit
class detailViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var yearLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
You can pass the data there when you create your UIViewController
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Show movie details
if let vc = storyboard?.instantiateViewController(identifier: "detailViewController") as? detailViewController{
vc.item = movies[indexPath.row]
self.navigationController?.pushViewController(vc, animated: true)
}
}
But you need to pass data first, and when you controller initialized (loaded) you can assign value to loaded views
class detailViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var yearLbl: UILabel!
var item: Movie? = nil
override func viewDidLoad() {
super.viewDidLoad()
if (let movie = item) {
titleLbl.text = item.title
yearLbl.tex = item.Year
// also image
}
}
}

Passing JSON to another view controller

I'm having a hard time trying to pass JSON to another view controller using a segue. So for I have only been able to use prepare(for segue, sender), but I can't get my data to populate my outlets on my view controller. Below is my first view controller. Within the prepare(for segue, sender) method you can see my commented out code that's not working. Any advice?
class ViewController2 : UIViewController, UITableViewDelegate, UITableViewDataSource {
var pictures : [Hit] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
NetworkMananger.shared.getInfo { [weak self] (results) in
guard let self = self else {return }
switch results {
case .failure(let error):
print(error)
case .success(let pictures):
self.pictures = pictures
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ViewController1"{
if let indexPath = self.tableView.indexPathForSelectedRow{
let vc = segue.destination as! ViewController1
// vc.downloadLabel = String(pictures[indexPath.row].downloads)
// vc.tagsLabel = pictures[indexPath.row].tags
// vc.imageData = UIImage(named: pictures[indexPath.row].previewURL)
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "ViewController1") as? ViewController1
navigationController?.pushViewController(vc!, animated: true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pictures.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell123", for: indexPath) as! firstTableViewCell
cell.label?.text = String(pictures[indexPath.row].downloads)
cell.downloadPictureFromURL(from: pictures[indexPath.row].previewURL)
return cell
}
}
Below is my second view controller I want to pass my JSON to :
class ViewController1: UIViewController {
var picture : Hit!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var imageData:
UIImageView!
#IBOutlet weak var tagsLabel: UILabel!
#IBOutlet weak var downloadLabel: UILabel!
var cap : String = ""
override func viewDidLoad() {
super.viewDidLoad()
}
}
JSON
struct Response : Codable {
let hits : [Hit]
}
struct Hit : Codable {
let tags : String
let previewURL : String
let downloads : Int
}
There are a couple of things getting in your way:
Right now, in your commented code, you have a type mismatch with each property you're trying to set. For example, you're trying to set a String to downloadLabel, which is a UILabel.
At the time of prepareForSegue, the IBOutlets may not be loaded.
To get around these issues, you could set properties on ViewController1 and then initialize the views in viewDidLoad:
struct ViewController1Input {
var downloadLabelText : String
var tagsLabelText: String
var imageName: String
}
class ViewController1: UIViewController {
var picture : Hit! //careful here -- I'm not sure where this is coming from and you aren't setting it in your segue
var input : ViewController1Input?
#IBOutlet weak var label: UILabel!
#IBOutlet weak var imageData: UIImageView!
#IBOutlet weak var tagsLabel: UILabel!
#IBOutlet weak var downloadLabel: UILabel!
var cap : String = ""
override func viewDidLoad() {
super.viewDidLoad()
if let input = input {
downloadLabel.text = input.downloadLabelText
tagsLabel.text = input.tagsLabelText
imageData.image = UIImage(named: input.imageName)
}
}
}
And in your segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ViewController1"{
if let indexPath = self.tableView.indexPathForSelectedRow{
let vc = segue.destination as! ViewController1
vc.input = ViewController1Input(downloadLabelText: String(pictures[indexPath.row].downloads),
tagsLabelText: pictures[indexPath.row].tags,
imageName: pictures[indexPath.row].previewURL)
}
}
}

Failed when present UIviewcontroller

I have 2 viewcontrollers: mainVC & listVC.
I tapped customized button on mainVC to present listVC, then tapped button on listVC to go back to mainVC. until now there is no any failure. But when I tap customized button to go to listVC again, program failed.
error:
NSInvalidArgumentException', reason: 'Application tried to present modal view controller on itself. Presenting controller is
Voice_of_Animals.ListVC: 0x7feeee513210>
class ViewController: UIViewController
{
var cb = CirButton(Circolor: UIColor.cyan, Rsize: 100, PositionX: 100, PositionY: 100)
override func viewDidLoad() {
super.viewDidLoad()
mainVC = self
self.view.addSubview(cb)
listVC.transitioningDelegate = listVC as UIViewControllerTransitioningDelegate
}
func showNextPage() {
self.present(listVC, animated: true, completion: nil)
}
#IBAction func show(_ sender: UIButton) {
self.present(listVC, animated: true, completion: nil)
}
#IBAction func Touch(_ sender: CButton) {
sender.RunAnimation()
}
override func didReceiveMemoryWarning() {
}
}
class ListVC: ViewController{
var transition = FadeAnimator()
var btn1 = CirButton(Circolor: UIColor.cyan, Rsize: 100, PositionX: 100, PositionY: 100)
#IBOutlet weak var myList: ListView!
#IBAction func Push(_ sender: UIButton) {
}
#IBAction func edgeSlide(_ sender: UIScreenEdgePanGestureRecognizer) {
mainVC.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(btn1)
}
}

Clicking Tableview Cell Does Not Display Data

I'm having trouble with my didSelectRowAt function of my tableview. For some reason, although my segue identifier and destination view are correct, when I click the row, it does not load anything. Furthermore, Xcode informs me:
Cannot assign value of type 'Double?' to type 'String?'
but I cannot find an online resource that tells me how to resolve this. For reference, the data I would like to display is from a nested JSON feed.
JSON Struct
struct PlayerStatsParent:Decodable{
let rankings: [PlayerStats]
}
struct PlayerStats:Decodable {
let personaname: String?
let score: Double?
let solo_competitive_rank: Int?
let avatar: String?
}
Cell Select Function
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? RankDetail {
destination.playerRank = rank[(rankTable.indexPathForSelectedRow?.row)!]
}
}
Destination View Controller Code
import UIKit
class RankDetail: UIViewController {
#IBOutlet var rankLabel: UILabel!
#IBOutlet var scoreLabel: UILabel!
var playerRank:PlayerStats?
override func viewDidLoad() {
super.viewDidLoad()
rankLabel.text = "\(playerRank?.solo_competitive_rank)"
scoreLabel.text = playerRank?.score
}
Also check your select method.Should be this.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
Not this;
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: self)
}
If you select the cell.
Looks like the error occurs in the RankDetail class.
Here's a simple fix.
class RankDetail: UIViewController {
#IBOutlet var rankLabel: UILabel!
#IBOutlet var scoreLabel: UILabel!
var playerRank:PlayerStats?
override func viewDidLoad() {
super.viewDidLoad()
rankLabel.text = "\(playerRank?.solo_competitive_rank)"
// scoreLabel.text if of type String?, so it can't store Double? directly
// scoreLabel.text = playerRank?.score
// converting to String? should fix it
scoreLabel.text = "\(playerRank?.score)"
}
EDIT:
Present ViewController using the following code. Saves you the trouble of having to worry about segues.
if let viewController = storyboard?.instantiateViewController(withIdentifier: "NewViewController") {
present(viewController, animated: true, completion: nil)
}

Using AlamoFire to fetch data to populate UITableView based on UISearchBar value

Unlike many JSON requests, instead of filtering a long pre-determined JSON, I intend to send a JSON request (using AlamoFire) based on what is typed into a Search Bar, thus fetching the JSON to be parsed (with SwiftyJSON) to fill the UITableView
What I'd like to do is to use the searchTerm (typed into the searchbar) to send an AlamoFire request to Geonames using the searchTerm in the query field of geonames API request. i.e.
http://api.geonames.org/searchJSON?formatted=true&q=(searchTerm)&maxRows=3&username=demo
What I essentially cannot do is send the request AFTER filling out the Search Bar, and therefore populate the table with that data.
Below is my current code (which populates the table upon the view loading with parsed JSON data with a request for "burpham"
import UIKit
import Alamofire
import SwiftyJSON
class SearchAddLocationViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
{
#IBOutlet weak var tblJSON: UITableView!
#IBOutlet weak var searchbarValue: UISearchBar!
weak open var delegate: UISearchBarDelegate?
var arrRes = [[String:AnyObject]]() //Array of dictionary
override func viewDidLoad()
{
super.viewDidLoad()
}
public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) // called when text ends editing
{
callAlamo(searchTerm: searchbarValue.text!)
}
func callAlamo(searchTerm: String)
{
Alamofire.request("http://api.geonames.org/searchJSON?q=\(searchTerm)&maxRows=5&username=garethallenstringer").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["geonames"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.tblJSON.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "addLocProtoCell")!
var dict = arrRes[indexPath.row]
cell.textLabel?.text = dict["name"] as? String
cell.detailTextLabel?.text = dict["countryName"] as? String
return cell
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return arrRes.count
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need to listen func searchBarTextDidEndEditing(_ searchBar: UISearchBar) method of UISearchBar delegate for populating your UITableView
class ViewController: UIViewController, UISearchBarDelegate {
#IBOutlet private weak var searchBar: UISearchBar?
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar?.delegate = self
}
//MARK: UISearchBarDelegate methods
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
//You can do your request via Alamofire to populate tableView
}
}
UPDATE #1
import UIKit
import Alamofire
import SwiftyJSON
class SearchAddLocationViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate
{
#IBOutlet weak var tblJSON: UITableView!
#IBOutlet weak var searchbarValue: UISearchBar!
var arrRes = [[String:AnyObject]]() //Array of dictionary
override func viewDidLoad()
{
super.viewDidLoad()
self.searchbarValue.delegate = self
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { // called when text ends editing
{
callAlamo(searchTerm: searchbarValue.text!)
}
func callAlamo(searchTerm: String)
{
Alamofire.request("http://api.geonames.org/searchJSON?q=\(searchTerm)&maxRows=5&username=garethallenstringer").responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["geonames"].arrayObject {
self.arrRes = resData as! [[String:AnyObject]]
}
if self.arrRes.count > 0 {
self.tblJSON.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "addLocProtoCell")!
var dict = arrRes[indexPath.row]
cell.textLabel?.text = dict["name"] as? String
cell.detailTextLabel?.text = dict["countryName"] as? String
return cell
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return arrRes.count
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}