swift _ Pushing ViewController in gives a black screen - swift

I have two screens the first one"ViewController" has tableview and the second "MapViewController" one contains MKMapView when i navigate from the first one to the second the second one looks like black screen the second screen is supposed to appear the apple map can any one help me ?
import UIKit
import MapKit
struct Category {
let place : String
let coordinates : [Double]
}
class ViewController: UIViewController {
private let tableview : UITableView={
let table = UITableView()
table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return table
}()
private let data : [Category]=[
Category(place: "Misr bank ", coordinates: [30.576352,31.503955])
]
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableview)
tableview.delegate=self
tableview.dataSource=self
}
override func viewDidLayoutSubviews() {
tableview.frame=view.bounds
}
}
extension ViewController:UITableViewDelegate{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let category = data[indexPath.row]
let mapview = MapViewController(coor: category.coordinates)
self.navigationController?.pushViewController(mapview, animated: true)
}
}
MapViewController
import UIKit
import MapKit
class MapViewController: UIViewController {
#IBOutlet weak var map: MKMapView!
private let coor : [Double]
init(coor:[Double]){
self.coor=coor
super.init(nibName: nil, bundle: nil)
let lat = coor[0]
let long = coor[1]
print(lat)
print(long)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}

It looks like MapViewController is designed in the storyboard. (If it were not, you would not be talking about #IBOutlet weak var map: MKMapView!)
But if so, then saying MapViewController(coor: category.coordinates) is not how to retrieve the instance that you designed in the storyboard. If you say that, you will indeed get basically an empty interface.
Instead, give that instance in the storyboard an identifier, and in your code, ask the storyboard to instantiate the view controller that has that identifier.
You will also have to rearrange your initialization completely. The storyboard is going to call init(coder:), not init(coor:). In fact, you can get rid of the latter entirely:
#IBOutlet weak var map: MKMapView!
var coor = [Double]()
required init?(coder: NSCoder) {
super.init(coder:coder)
}
override func viewDidLoad() {
super.viewDidLoad()
let lat = coor[0]
let long = coor[1]
print(lat)
print(long)
}
Now when you instantiate MapViewController from the storyboard, immediately also set mapView.coor before pushing.

Related

Table Content disappears on Scroll in TableView with Custom Cell using Subview - Swift

I have a ViewController which uses multiple Subviews (HomeViewController, etc.) which can be selected via a Custom Tab Bar at the bottom of my app. Inside the HomeViewController there is a UIView containing a UITableView containing a Prototype Custom Cell with name and image.
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var friendView: UITableView!
let friends = ["batman", "harsh", "ava", "sasha", "fatima", "alfred"]
override func viewDidLoad() {
super.viewDidLoad()
friendView.delegate = self
friendView.dataSource = self
friendView.allowsSelection = false
}
}
extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friends.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = friendView.dequeueReusableCell(withIdentifier: "customCell") as! CustomCell
let friend = friends[indexPath.row]
cell.avatarImg.image = UIImage(named: friend)
cell.nameLbl.text = friend
return cell
}
}
Custom cell:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var friendView: UIView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var avatarImg: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
When I start the app, everything looks just fine. However, when I start scrolling inside the table, all data suddenly disappears. All relations between storyboard and code should be just fine. I think it might have got something to do with my need of using a Subview.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tabBarView: UIView!
#IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
Design.makeCornersRound(view: tabBarView, radius: 10.0)
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
self.switchToHomeViewController()
}
}
#IBAction func onClickTabBar(_ sender: UIButton) {
let tag = sender.tag
if tag == 1 {
switchToIncomingsViewController()
}
else if tag == 2 {
switchToSpendingsViewController()
}
else if tag == 3 {
switchToHomeViewController()
}
else if tag == 4 {
switchToSavingsViewController()
}
else if tag == 5 {
switchToSettingsViewController()
}
}
func switchToHomeViewController() {
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
contentView.addSubview(Home.view)
Home.didMove(toParent: self)
}
...
}
Reference to the tutorial I have been trying to implement: https://www.youtube.com/watch?v=ON3Z0PXSoVk
In this function:
func switchToHomeViewController() {
// 1
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// 2
contentView.addSubview(Home.view)
// 3
Home.didMove(toParent: self)
// 4
}
At 1 you create an instance of HomeViewController
at 2 you add its view to cotentView
at 3 you call didMove() ... but that doesn't do anything because you haven't added the controller to your hierarchy
at 4 your Home instance goes away, so the code in that controller no longer exists
You need to add the controller as a child controller.
As a side note, use lowerCase for variable names:
func switchToHomeViewController() {
// create an instance of HomeViewController
guard let homeVC = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// add it as a child view controller
self.addChild(homeVC)
// add its view
contentView.addSubview(homeVC.view)
// here you should either set the view's frame or add constraints
// such as:
homeVC.view.frame = contentView.bounds
// inform the controller that it moved to a parent controller
homeVC.didMove(toParent: self)
}

Beginner question on passing data between view controllers

I am trying to recreate the Notes app in iOS. I have created an initial View Controller which is just a table view. A user can go to a Detail View Controller to compose a new note with a Title and Body section. When they click Done, I want to manipulate the tableView with note's details.
I am struggling saving the details of what the user entered to use on my initial view controller.
Here's my Notes class which defines the notes data:
class Notes: Codable {
var titleText: String?
var bodyText: String?
}
Here is the Detail View controller where a user can input Note details:
class DetailViewController: UIViewController {
#IBOutlet var noteTitle: UITextField!
#IBOutlet var noteBody: UITextView!
var noteDetails: Notes?
var noteArray = [Notes]()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(updateNote))
noteTitle.borderStyle = .none
}
#objc func updateNote() {
noteDetails?.titleText = noteTitle.text
noteDetails?.bodyText = noteBody.text
noteArray.append(noteDetails!) // This is nil
// not sure if this is the right way to send the details over
// let vc = ViewController()
// vc.noteArray.append(noteDetails!)
if let vc = storyboard?.instantiateViewController(identifier: "Main") {
navigationController?.pushViewController(vc, animated: true)
}
}
}
I also have an array on my initial view controller as well. I think I need this one to store note data to display in the tableView (and maybe don't need the one on my Detail View controller?). The tableView is obviously not completely implemented yet.
class ViewController: UITableViewController {
var noteArray = [Notes]()
override func viewDidLoad() {
super.viewDidLoad()
print(noteArray)
self.navigationItem.setHidesBackButton(true, animated: true)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(composeNote))
}
#objc func composeNote() {
if let dvc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {
navigationController?.pushViewController(dvc, animated: true)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
noteArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
Just using Delegate:
First create delegate protocol with a func to send back note to your viewController
protocol DetailViewControllerDelegate: AnyObject {
func newNoteDidAdded(_ newNote: Note)
}
Next add the delegate variable to DetailViewController, and call func noteDataDidUpdate to send data back to viewController
class DetailViewController: UIViewController {
weak var delegate: DetailViewControllerDelegate?
#objc func updateNote() {
....
delegate?.newNoteDidAdded(newNote)
}
}
finally, set delegate variable to viewController and implement this in ViewController
class ViewController: UIViewController {
....
#objc func composeNote() {
if let dvc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {
dvc.delegate = self
navigationController?.pushViewController(dvc, animated: true)
}
}
}
extension ViewController: DetailViewControllerDelegate {
func newNoteDidAdded(_ newNote: Note) {
// do some thing with your new note
}
}

Open viewcontroller with textfield inside view xib file

I will explain the process before asking my question:
I have a tableviewcontroller, but i needed a textfield was always present at the hearing, to solve this problem i create a xib file with textfield inside and call the view with viewForHeaderInSection; at this point all is perfect.
i call the view whith this code:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return NSBundle.mainBundle().loadNibNamed("createComment", owner: nil, options: nil)[0] as? createComment
}
My problem is next:
I want that when capturing text field starts a new window opens, something like what makes facebook to create a comment.
My code in createComment is:
class createComment: UIView, UITextFieldDelegate {
#IBOutlet weak var commentTextField: StyleComentTextField!
weak var controller:tableComentVC!
var currentController = tableComentVC()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
self.commentTextField.delegate = self
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
commentTextField.resignFirstResponder()
if(textField == commentTextField){
print("ok i tab in commentTextField")
let storyBoard: UIStoryboard = UIStoryboard (name: "Main", bundle: nil)
self.controller?.navigationController!.pushViewController(storyBoard.instantiateViewControllerWithIdentifier("writeComment") as! writeComment, animated: true)
resign()
return false
}else{
return true
}
}
}
but this code dont work, the print in code works correctly, but i don't know why dont open the next viewcontroller.
Thanks!

Proper style in Swift: Guard Statement

I am getting my feet wet with Swift and I am wondering what proper style for guards is. Am I using the guard statement correctly? It feels a little clunky, but I prefer it to an if let statement. Or could this be simplified?
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView?
...
required init() {
super.init(nibName:nil, bundle:nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
// MARK: - User Interface
func setupUI() {
...
// tableView
tableView = UITableView()
guard let tableView = tableView else {
Log.msg("tableView could not be initialized")
return
}
tableView.delegate = self
tableView.dataSource = self
tableView.separatorInset = UIEdgeInsetsZero
tableView.layoutMargins = UIEdgeInsetsZero
tableView.accessibilityIdentifier = "tableView"
tableView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(tableView)
...
}
}
Log is a custom logging class that has optimized console output.
Although I think this question should be moved to the codereview site, I'll mention this:
You are using the guard statement correctly from a syntactic point of view. It's a way to check something and return early if the condition isn't met. Your overall implementation needs a bit of refactoring though so you don't need the guard at all.
When creating a view controller, do this instead:
class ViewController: UIViewController {
private var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView = {
let tableView = UITableView()
tableView.property = ...
return tableView
}()
view.addSubview(tableView)
// setup constraints on tableView...
}
}
By using an implicitly unwrapped optional for your tableView property you forego the need for a guard and testing the tableView for nil wherever you need to use it, but you also get the benefit of deferred initialization (in viewDidLoad()).

Need to update tableView

I'm studying programming in Swift, and I've got this example from a book with a mistake (commented). How can I fix this and update tableView while pressing the Add button? I've read many solutions, but none seem to work. Also, tableView updated when I reloaded simulator. I've been stuck with this for two days, and it's driving me mad! :)
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var managedObjectContext: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
managedObjectContext = appDelegate.managedObjectContext! as NSManagedObjectContext
// 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.
}
func loadBooks() -> Array<AnyObject> {
var error: NSError? = nil
var fetchRequest = NSFetchRequest(entityName: "Book")
let result: [AnyObject] = managedObjectContext!.executeFetchRequest(fetchRequest, error:&error)!
return result
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return loadBooks().count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
var book: Book = loadBooks()[indexPath.row] as! Book
cell.textLabel!.text = book.title
return cell
}
#IBAction func addNew(sender: AnyObject) {
let entity = NSEntityDescription.entityForName("Book", inManagedObjectContext:managedObjectContext)
var book = Book(entity: entity!,insertIntoManagedObjectContext:managedObjectContext)
book.title = "My Book:" + String(loadBooks().count)
var error: NSError?
managedObjectContext.save(&error)
myTableView.reloadData() //mistake!
}
}
You have to declare your an outlet for your tableview which is probably set up in the according storyboards view controller.
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var managedObjectContext: NSManagedObjectContext!
#IBOutlet weak var myTableView: UITableView!
// your code
}
Please don't forget to connect your table view instance from the storyboard with your new created outlet, by opening code and storyboard side-by-side and draw a line from the dot next to the outlet to the tableview.
Or you can use the way apple describes here (https://developer.apple.com/library/ios/recipes/xcode_help-IB_connections/chapters/CreatingOutlet.html) to create your outlet from the table view on the storyboard.
edit:
Nevermind, i realized that if you wouldnt have had connected the dataSource and delegate, the crash would have happend way before the addNew().
Kie's answer is correct, however to see an actual result you also need to connect the dataSource and delegate of the tableView to your class.
You can do this either in storyboard, or in code in the viewDidLoad method.
override func viewDidLoad(){
super.viewDidLoad()
myTableView.dataSource = self
myTableView.delegate = self
// Additional code
}