How to link storyboard to view controller - ios

I am trying to connect a storyboard which I made to my view controller file in my chat app.
What I did is...
1) Create a view controller file(swift).
2) Create a storyboard file and set view controller class as a custom class.
3) Connect my component(table view) in my storyboard to a view controller as an outlet.
Now, I got "Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value" error message.
Code is the following.
import UIKit
class ChatViewController: UIViewController {
var _device_width: Int = 0
var _device_height: Int = 0
var _footer_size: Int = 0
var _backBtn: UIBarButtonItem!
var window: UIWindow?
var _toolBar: UIToolbar!
var bottomView: ChatRoomInputView!
var chats: [ChatEntity] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
showNavigateBar()
setupUI()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override var canBecomeFirstResponder: Bool {
return true
}
override var inputAccessoryView: UIView? {
return bottomView
}
func showNavigateBar() {
let backButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationItem.backBarButtonItem = backButtonItem
self.title = 'My App'
}
}
extension ChatViewController {
func setupUI() {
self.view.backgroundColor = UIColor(red: 113/255, green: 148/255, blue: 194/255, alpha: 1)
// ERROR happen on this line.
// "Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"
tableView.separatorColor = UIColor.clear
tableView.estimatedRowHeight = 10000
tableView.rowHeight = UITableViewAutomaticDimension
tableView.allowsSelection = false
tableView.keyboardDismissMode = .interactive
tableView.register(UINib(nibName: "YourChatViewCell", bundle: nil), forCellReuseIdentifier: "YourChat")
tableView.register(UINib(nibName: "MyChatViewCell", bundle: nil), forCellReuseIdentifier: "MyChat")
let chat1 = ChatEntity(text: "text1", time: "10:01", userType: .I)
let chat2 = ChatEntity(text: "text2", time: "10:02", userType: .You)
let chat3 = ChatEntity(text: "text3", time: "10:03", userType: .I)
chats = [chat1, chat2, chat3]
}
}
extension ChatViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.chats.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let chat = self.chats[indexPath.row]
if chat.isMyChat() {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyChat") as! MyChatViewCell
cell.clipsToBounds = true
// Todo: isRead
cell.updateCell(text: chat.text, time: chat.time, isRead: true)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourChat") as! YourChatViewCell
cell.clipsToBounds = true
cell.updateCell(text: chat.text, time: chat.time)
return cell
}
}
}
extension ChatViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath)
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 10
}
}
And I defined view controller as a custom class.
What should I do to work correct?
Please give me an advice.
ADD)
ChatViewController is called by another class file.
let chatViewController = ChatViewController()
self.navigationController?.pushViewController(chatViewController, animated: false)
And I believe #IBOutlet is connect correctly.
ChatViewController.swift
ChatViewController.storyboard
And I know the tableView is nil when this function was called.
Actually, I don't know much about the storyboard because I usually don't use storyboard. I prefer to create UI by the code. But now, I have to use storyboard file because I want to use a sample project of the chat system.
ADD2)
I think I may mistake the setting of the storyboard. I have no idea how to use navigation controller or entry point on the storyboard.

You should add delegate and datasource in your viewLoad() function.
override func viewDidLoad() {
super.viewDidLoad()
showNavigateBar()
tableView.delegate = self
tableView.dataSource = self
setupUI()
}
If error still exist after that, check your "Outlets" in your Storyboard.

I fixed the problem.
I modified call method.
let storyBoard : UIStoryboard = UIStoryboard(name: "ChatViewController", bundle:nil)
let chatViewController = storyBoard.instantiateViewController(withIdentifier: "ChatViewController")
self.navigationController?.pushViewController(chatViewController, animated: true)
Thank you for your kindness advice.

Related

I want to modal tableView Cell data backward to the main View

I want to modalViewController's table cell data pass to the main View.
Using Protocol delegate. but
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
This error occur where i remark at the ModalViewController.
Using Protocol method is wrong?.
I use this protocol code another mini project textField's text data pass back and forward. In there it works well.
So i use it in cell data pass
What am i Miss ? Please help.
Bottom Link is my Simulator.
ModalTableViewDataPass
**MainViewController**
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var show: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
show.placeholder = "HAHA"
show.delegate = self
show.inputView = UIView()
show.addTarget(self, action: #selector(textFieldDidBeginEditing(_:)), for: .touchUpInside)
// Do any additional setup after loading the view.
}
#objc func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == show {
moveToFindAdrVC()
print("주소 검색해라잉")
}
}
func moveToFindAdrVC() {
let modalVC = storyboard?.instantiateViewController(identifier: "ModalViewController") as? ModalViewController
self.present(modalVC!, animated: true, completion: nil)
}
}
extension ViewController: PassDataToVc {
func passData(str: String){
show.text = str
}
}
ModalTableView
import UIKit
class ModalViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var data = ["as","df","qw","er"]
var delegate: PassDataToVc!
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = data[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let passData = data[indexPath.row]
let sb = storyboard?.instantiateViewController(identifier: "ViewController") as? ViewController
//sb?.show.text = passData
delegate.passData(str: passData)//Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
self.dismiss(animated: true, completion: nil)
}
}
protocol PassDataToVc {
func passData(str: String)
}
delegate in ModalViewController is never set, hence it's nil.
But there is a more convenient solution, protocol/delegate is not needed at all.
Delete the code related to protocol/delegate
extension ViewController: PassDataToVc {
func passData(str: String){
show.text = str
}
}
var delegate: PassDataToVc!
and replace
delegate.passData(str: passData)
with
(presentingViewController as? ViewController)?.show.text = passData

tableview sent me an error when I tried to present a uiview in front of it

I'm new with Xcode and Swift, following a tutorial and I found a problem when I called a UIView in front of a tableview so the user can create something new
NOTE: I already tried what this link shows with no luck to resolve my issue
I am using Xcode 11.3.1 and Swift
This is my code
Channel Model
import Foundation
struct Channel : Decodable {
public private(set) var channelTitle: String!
public private(set) var channelDescription: String!
public private(set) var id: String!
}
Class ChannelCell
import UIKit
class ChannelCell: UITableViewCell {
// Outlets
#IBOutlet weak var channelName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
self.layer.backgroundColor = UIColor(white: 1, alpha: 0.2).cgColor
} else {
self.layer.backgroundColor = UIColor.clear.cgColor
}
}
func configureCell(channel: Channel) {
let title = channel.channelTitle ?? ""
channelName.text = "#\(title)"
}
}
Channel View Controller
import UIKit
class ChannelVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Outlets
#IBOutlet weak var loginBtn: UIButton!
#IBOutlet weak var userImg: CircleImage!
#IBOutlet weak var tableView: UITableView!
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self <<<<<<<<< here I get the error message ***
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
tableView.dataSource = self
self.revealViewController()?.rearViewRevealWidth = self.view.frame.size.width - 60
NotificationCenter.default.addObserver(self, selector: #selector(ChannelVC.userDataDidChange(_:)), name: NOTIF_USER_DATA_DID_CHANGE, object: nil)
}
override func viewDidAppear(_ animated: Bool) {
setupUserInfo()
}
When I pressed the add channel button comes the problem
#IBAction func addChannelPressed(_ sender: Any) {
if AuthService.instance.isLoggedIn {
let addChannel = ChannelVC()
addChannel.modalPresentationStyle = .custom
present(addChannel, animated: true, completion: nil)
} else {
performSegue(withIdentifier: TO_LOGIN, sender: nil)
}
}
#IBAction func loginBtnPressed(_ sender: Any) {
if AuthService.instance.isLoggedIn {
let profile = ProfileVC()
profile.modalPresentationStyle = .custom
present(profile, animated: true, completion: nil)
} else {
performSegue(withIdentifier: TO_LOGIN, sender: nil)
}
}
#objc func userDataDidChange(_ notif: Notification) {
setupUserInfo()
}
func setupUserInfo() {
if AuthService.instance.isLoggedIn {
loginBtn.setTitle(UserDataService.instance.name, for: .normal)
userImg.image = UIImage(named: UserDataService.instance.avatarName)
userImg.backgroundColor = UserDataService.instance.returnUIColor(components: UserDataService.instance.avatarColor)
} else {
loginBtn.setTitle("Login", for: .normal)
userImg.image = UIImage(named: "menuProfileIcon")
userImg.backgroundColor = UIColor.clear
}
}
// Protocols for UITableViewDataSource
// # of sections
// # rows in the section
// function to setup the cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "channelCell", for: indexPath) as? ChannelCell {
I double check the reusable identifier is OK
let channel = MessageService.instance.channels[indexPath.row]
cell.configureCell(channel: channel)
return cell
} else {
return UITableViewCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if MessageService.instance.channels.count == 0 {
tableView.setEmptyView(title: "Message!", message: "You don´t have any channel, create a new one")
}
return MessageService.instance.channels.count
}
}
this is the view I want to show when I click on the addChannel function
view to present
and this is the debug area
debug area
You are making a very common mistake. The line
let addChannel = ChannelVC()
creates a new instance of the controller which is not the instance in the storyboard. Therefore the outlets are not connected and the code crashes.
Replace it with (adjust the identifier accordingly)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let addChannel = storyboard.instantiateViewController(withIdentifier: "ChannelVC") as! ChannelVC
or create a segue.

I can not solve the error "coding classification not key value coding-compliant for the key"

It is a question.
This class is not key value code-compliant for the key error is given.
However, I am not sure which part is wrong. This error appears at cellForRowAt.
Thank you for your answer.
If you want it, I can also show the code of ContentTableViewCell, so please call out.
import UIKit
class SearchViewController: UIViewController, UISearchResultsUpdating {
#IBOutlet weak var tableview: UITableView!
var songs = [
"裁量権を持って、若者向けカレンダーアプリ開発をしたいiOSエンジニア募集!",
"自信を持って、応援出来るエンジニアを募集!"
]
var searchSongs = [String]()
func updateSearchResults(for searchController: UISearchController) {
let searchString = searchController.searchBar.text!
searchSongs = songs.filter {(name) -> Bool in
return name.contains(searchString)
}
tableview.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
let searchController = UISearchController(searchResultsController:nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
navigationItem.searchController = searchController
definesPresentationContext = true
delegate()
}
}
extension searchViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if navigationItem.searchController?.isActive == true {
return searchSongs.count
} else {
return songs.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "ContentTableViewCell", for: indexPath) as! ContentTableViewCell
if navigationItem.searchController?.isActive == true {
cell.commonInit(content: searchSongs[indexPath.row])
} else {
cell.commonInit(content: songs[indexPath.row])
}
return cell
}
func delegate() {
tableview.delegate = self
tableview.dataSource = self
let nibName = UINib(nibName: "ContentTableViewCell", bundle: nil)
self.tableview.register(nibName, forCellReuseIdentifier: "ContentTableViewCell")
}
}
Check in your Storyboard. I think any outlet is not connected properly.
Check is there any yellow warning triangle in your storyboard on which screen this error is coming. If you found any, remove and connect the outlet again.

design UI of app using tablview in swift

I want UI of my app to be similar like as shown in below image.
Using tableview i'm able to get Payment,Delivery,Build Team options as shown in image but for Build Profile,Manage Menu.. how do i do it using tableview ?
If not tableview for these options then what other control i can try.
this type of UI you are looking for and you can easily get by creating different Cell classes
UI Output :
Required Code
Step 1 - Create different Xib of table cell
class viewWithArrow: UITableViewCell {
#IBOutlet weak var headerLabel: UILabel!
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
}
}
class viewWithShadow: UITableViewCell {
#IBOutlet weak var shadowView: UIView!{
didSet{
shadowView.addShadowToView(color: .black)
}
}
#IBOutlet weak var headerLabel: UILabel!
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
}
}
Adding Shadow Effect :
extension UIView {
func addShadowToView(color:UIColor)
{
self.layer.shadowColor = color.cgColor
self.layer.shadowOpacity = 0.2
self.layer.masksToBounds = false
self.clipsToBounds = false
self.layer.shadowOffset = CGSize(width: 0, height: 0)
self.layer.shadowRadius = 5
}
}
View controller
class ViewController: UIViewController
{
var dataArray : [String] = ["11","22","33","44","55","66"]
#IBOutlet weak var homeVcTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
homeVcTableView.register(UINib(nibName: "viewWithShadow", bundle: nil), forCellReuseIdentifier: "viewWithShadow")
homeVcTableView.register(UINib(nibName: "viewWithArrow", bundle: nil), forCellReuseIdentifier: "viewWithArrow")
}
override func viewWillAppear(_ animated: Bool) {
self.homeVcTableView.delegate = self
self.homeVcTableView.dataSource = self
}
}
extension ViewController : UITableViewDataSource, UITableViewDelegate
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0,1:
let cell = self.homeVcTableView.dequeueReusableCell(withIdentifier: "viewWithShadow", for: indexPath) as! viewWithShadow
cell.headerLabel.text = dataArray[indexPath.row]
return cell
default:
let cell = self.homeVcTableView.dequeueReusableCell(withIdentifier: "viewWithArrow", for: indexPath) as! viewWithArrow
cell.headerLabel.text = dataArray[indexPath.row]
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.homeVcTableView.frame.size.height*0.1
}
}
Working code link - https://drive.google.com/open?id=18mSlWAU_e4XElox4H-oTpqg8clD_HKwu

Why is my tableView variable returning nil?

I am using a UISearchController in my project. I initiate the search controller by supplying the init(searchResultsController) method with an UIViewController object that manages a tableView. The code for this object is:
import UIKit
class ResultsTableViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var list: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ResultsTableViewController: UITableViewDelegate{}
extension ResultsTableViewController: UITableViewDataSource{
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")
if cell == nil {
cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell")
cell?.textLabel?.text = list[indexPath.row]
} else {
cell?.textLabel!.text = list[indexPath.row]
}
return cell!
}
}
Furthermore, when I try to access the resultsTableViewController.tableView from the updateSearchResultsForSearchController(searchController: UISearchController) method of the UISearchResultsUpdating protocol to populate its "list" array, it gives me an error. The tableView returns nil and the app crashes. I would like to point out that I have connected the data source, delegate, and the IBOutlet variable of the tableView to the appropriate view controller. I was hoping for someone to explain to me why this happens? I think I have a misunderstanding in the life cycle of the ResultsTableViewController. Lastly, when I drag a TableViewController from the storyboard instead of making my own table view controller from scratch everything works smoothly without any errors! Can you please help me understand whats going on here?
Edit: The code for my initial view controller is:
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var resultsTableViewController = ResultsTableViewController()
var searchController: UISearchController!
let list = ["Apple", "Orange", "Bananas","Kiwi", "Cucumbers", "Apricot","Peach", "Cherry", "Mangosteen","Strawberry", "Blueberry", "Raspberry","Watermelon", "Persimmon", "plums","Papaya", "Jackfruit", "Lichi"]
var filteredList: [String]!
override func viewDidLoad() {
super.viewDidLoad()
setUpSearchController()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func setUpSearchController(){
searchController = UISearchController(searchResultsController: resultsTableViewController)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = true
tableView.tableHeaderView = searchController.searchBar
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
}
}
extension FirstViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell")
if cell == nil {
cell = UITableViewCell(style: .Default, reuseIdentifier: "cell")
cell?.textLabel?.text = list[indexPath.row]
}else {
cell?.textLabel?.text = list[indexPath.row]
}
return cell!
}
}
extension FirstViewController: UISearchResultsUpdating{
func updateSearchResultsForSearchController(searchController: UISearchController) {
filteredList = list.filter({
item in
return item.containsString(searchController.searchBar.text!)
})
resultsTableViewController.list = filteredList
resultsTableViewController.tableView.reloadData()
}
}
var resultsTableViewController = ResultsTableViewController()
creates a new ResultsTableViewController but it isn't linked to your storyboard scene, so none of the #IBOutlets will be set. You need to set an identifier for your scene (say resultsViewController) and then use that to instantiate the view controller from the storyboard:
var resultsTableViewController: ResultsTableViewController!
override func viewDidLoad() {
super.viewDidLoad()
setUpSearchController()
}
func setUpSearchController(){
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
self.resultsTableViewController = storyboard.instantiateViewControllerWithIdentifer("resultsViewController") as! ResultsTableViewController
searchController = UISearchController(searchResultsController: resultsTableViewController)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = true
tableView.tableHeaderView = searchController.searchBar
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
}

Resources