Handling Navigation controllers with multiple storyboards - ios

I have a navigationController in my first storyboard. From that storyboard, I push a new viewController onto the stack but its in a different storyboard. When I try to change the background image for my secondviewController only, it doesn't work.
In my first viewController, I programmatically segue to UsersViewController, which is my second VC. Here is my first view controller and storyboard:
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var welcomeLabel: UILabel!
#IBOutlet weak var splitLabel: UIButton!
#IBOutlet weak var rouletteLabel: UIButton!
#IBOutlet weak var quickLabel: UIButton!
#IBOutlet weak var buttonStackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
welcomeLabel.font = UIFont(name: Theme.headingFontName, size: 32)
splitLabel.titleLabel?.font = UIFont(name: Theme.mainFontName, size: 26)
splitLabel.backgroundColor = Theme.accent
splitLabel.layer.cornerRadius = 10
rouletteLabel.titleLabel?.font = UIFont(name: Theme.mainFontName, size: 26)
rouletteLabel.backgroundColor = Theme.delete
rouletteLabel.layer.cornerRadius = 10
quickLabel.titleLabel?.font = UIFont(name: Theme.mainFontName, size: 26)
quickLabel.backgroundColor = Theme.edit
quickLabel.layer.cornerRadius = 10
}
#IBAction func splitNavigation(_ sender: Any) {
let storyboard = UIStoryboard(name: String(describing: UsersViewController.self), bundle: nil)
let vc = storyboard.instantiateInitialViewController()!
navigationController?.pushViewController(vc, animated: true)
}
}
In my UsersViewController, I try to change the background image of the navigationbar:
import UIKit
class UsersViewController: UIViewController {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var viewContainer: UIView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var nextBtn: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
setup()
}
func setup() {
view.backgroundColor = Theme.background
nameTextField.borderStyle = .none
nameTextField.backgroundColor = .clear
nameTextField.underlined()
viewContainer.backgroundColor = .white
viewContainer.layer.cornerRadius = 40
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.title = "People"
self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: "navBar"), for: .default)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toAddUserVC" {
let popup = segue.destination as! AddUsersViewController
popup.doneSaving = { [weak self] in
self?.tableView.reloadData()
}
}
}
}
extension UsersViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Data.userModels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! UsersTableViewCell
let model = Data.userModels[indexPath.row]
cell.setup(userModel: model)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
}
extension UITextField {
func underlined(){
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.lightGray.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
}
I don't know why, but every time I navigate to the UsersViewController, the background image flashes for a quick second, and then disappears. The title and bar buttons still show. Then, if I navigate back to the first viewController, the NavigationBar is gone. Here is a gif of what happens when I try to run the code if that helps.
via GIPHY
I'm able to change the color, the translucence, and every other property of the navigationbar, expect this. I figure it something to do with the fact that it's in a different storyboard. Any help is appreciated! Thank you!

Related

What am I doing wrong on passing data using Protocols

I'm trying to find a "cleaner-elegant" way to pass data between UIViewControllers. So, I decided to proceed using Delegates and Protocols. However, I failed on receive the data provided by my Protocol. What am I doing wrong?
Trying to receive the protocol data and use it to populate a UITableView:
class ViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource, dataReceivedDelegate {
func dataReceived(nome: String, foto: UIImage, qtd: Int) {
nomeReceived = nome
self.qtd = qtd
self.itensTableView.reloadData()
}
#IBOutlet weak var itensTableView: UITableView!
var arrayNomes = NSMutableArray()
var nomeReceived = ""
var qtd:Int = 0
var objetos = [Objeto]()
//TableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// let item = objetos[indexPath.row]
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = nomeReceived //Nil value
// cell.imageViewCell.image = item.foto //Nil value
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return qtd
}
override func viewDidAppear(_ animated: Bool) {
let controller = storyboard?.instantiateViewController(withIdentifier: "addVc") as! adicionarNovoItemVc
controller.delegate = self
}
Creating and setting value to the Protocol:
import UIKit
protocol dataReceivedDelegate {
func dataReceived(nome:String,foto:UIImage,qtd:Int)
}
class adicionarNovoItemVc: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {
#IBOutlet weak var textFieldNome: UITextField!
let imagePicker = UIImagePickerController()
#IBOutlet weak var namePreview: UILabel!
#IBOutlet weak var imagePreview: UIImageView!
let picker = UIImagePickerController()
var delegate:dataReceivedDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.textFieldNome.delegate = self
// Do any additional setup after loading the view.
}
#IBAction func botaoAdcItem(_ sender: UIButton) {
if (self.namePreview!.text != nil) && (self.imagePreview!.image != nil) {
delegate?.dataReceived(nome: self.namePreview.text!, foto: self.imagePreview.image!, qtd: 1)
self.navigationController?.popViewController(animated: true)
}
else {return}
}
In your ViewController add an action to button,
func buttonAction(sender: UIButton!) {
let storyboard = UIStoryboard.init(name: "yourStoryboarName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "addVc") as! adicionarNovoItemVc
controller.delegate = self
self.navigationController?.pushViewController(controller, animated: true)
}
Once new controller is pushed on screen, you can execute 'botaoAdcItem' action and rest will get you expected result.

Updating Label in Cell

I have a TableView which rows contain label and two buttons. What I wanna do is that when a user clicks the first button "Set Name", a pop up view comes up in which he can input text from keyboard. After hitting "Set", pop up view is dismissed and label inside a row containing the clicked button changes to the input text. I set the delegates but I cannot make label to change.
TableView:
import UIKit
class SetGame: UIViewController, UITableViewDelegate, UITableViewDataSource
{
var numOfPlayers = Int()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return numOfPlayers
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.Name.text = "Player \(indexPath.row + 1)"
cell.btn1.tag = indexPath.row
cell.btn2.tag = indexPath.row
return cell
}
override func viewDidLoad()
{
super.viewDidLoad()
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.none
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
#IBAction func setName(sender: UIButton)
{
let thisVC = storyboard?.instantiateViewController(withIdentifier: "SetName") as! SetName
thisVC.delegate = self
present(thisVC, animated: true, completion: nil)
}
#IBAction func setFingerprint(_ sender: UIButton)
{
}
#IBAction func unwindToSetGame(_ segue: UIStoryboardSegue)
{
print("unwinded to SetGame")
}
#IBOutlet weak var tableView: UITableView!
}
extension SetGame: nameDelegate
{
func named(name: String)
{
let indexP = IndexPath(row: 0, section: 0)
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexP) as! TableViewCell
cell.Name.text = "bkjhvghcjhkv"
//wanted to see if it changes first cell. But doesn't work
}
}
TableViewCell Class:
import UIKit
class TableViewCell: UITableViewCell
{
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
#IBOutlet weak var Name: UILabel!
#IBOutlet weak var btn1: UIButton!
#IBOutlet weak var btn2: UIButton!
}
Pop up View:
import UIKit
protocol nameDelegate
{
func named(name: String)
}
class SetName: UIViewController
{
var delegate: nameDelegate!
override func viewDidLoad()
{
super.viewDidLoad()
window.layer.borderWidth = 1
window.layer.borderColor = UIColor.white.cgColor
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
#IBAction func closePopUp(_ sender: Any)
{
if input.text != ""
{
delegate.named(name: input.text!)
}
dismiss(animated: true, completion: nil)
}
#IBOutlet weak var input: UITextField!
#IBOutlet weak var window: UIView!
}
Replace this
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexP) as! TableViewCell
with
let cell = tableView.cellForRow(at:indexP) as! TableViewCell

BEMcheckbox check/uncheck issue in tableview in swift 3

I am using BEMcheckbox. when i click it, it animates and show a hidden label but when I scroll my tableview my checkbox is automatically deselected. also when I scroll it doesn't select any checkbox automatically. what I want is when I scroll my tableview the checkbox which are checked remains checked and which are unchecked remains unchecked. my code is below. my view controller class.
class markAttendanceViewController: UIViewController , UITableViewDataSource , UITableViewDelegate{
#IBAction func selectall(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
checkImageView.isHidden = false
checkboxLabel.layer.borderColor = UIColor.blue.cgColor
} else{
checkImageView.isHidden = true
checkboxLabel.layer.borderColor = UIColor.lightGray.cgColor
}
table.reloadData()
}
#IBOutlet weak var checkImageView: UIImageView!
#IBOutlet weak var checkboxLabel: UILabel!
#IBAction func backToAttendanceView(_ sender: AnyObject) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var controller: UIViewController!
controller = storyboard.instantiateViewController(withIdentifier: "listViewController") as! listViewController
(controller as! listViewController).receivedString = "Mark Attendance"
let navController = UINavigationController(rootViewController: controller)
let revealController = self.revealViewController() as! RevealViewController
revealController.rightViewController = navController
revealController.rightViewController.view.addGestureRecognizer(revealController.panGestureRecognizer())
self.present(revealController, animated: true, completion: nil)
}
#IBOutlet weak var table: UITableView!
#IBOutlet weak var button: UIButton!
//var items:Array = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
checkboxLabel.layer.borderWidth = 1
checkboxLabel.layer.borderColor = UIColor.lightGray.cgColor
// items = ["Dashboard","Mark Attendance","Update Attendance","delete Attendance","Time Table","Academic Calendar","Reports","About Us","Logout","rbivwe","whefo","ewsow","webkgwo","wbiebfkwbei","ejwvabei","vdkgdvkJDB"]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "attendanceTableViewCell") as! attendanceTableViewCell
// cell.studentname?.text = items[indexPath.row]
cell.serialnumber?.text = "\(indexPath.row + 1)"
if button.isSelected {
cell.present.isHidden = false
cell.box.setOn(true, animated: true)
} else
{
cell.box.setOn(false, animated: false)
cell.present.isHidden = true
}
return cell
}
}
My tableview cell class.
class attendanceTableViewCell: UITableViewCell,BEMCheckBoxDelegate {
#IBOutlet weak var present: UILabel!
#IBOutlet weak var box: BEMCheckBox!
#IBOutlet weak var studentname: UILabel!
#IBOutlet weak var serialnumber: UILabel!
#IBOutlet weak var view: UIView!
override func awakeFromNib() {
box.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
view.layer.masksToBounds = false
view.layer.cornerRadius = 2.0
view.layer.shadowOffset = CGSize(width: -1, height: 1)
view.layer.shadowOpacity = 0.2
// Configure the view for the selected state
}
func didTap(_ checkBox: BEMCheckBox) {
if box.on {
present.isHidden = false
} else {
present.isHidden = true
}
}
}
If someone still need a solution for this. The only way I got it work is to add checkBox state each time you tap on it and then check the state in cellForRowAt function. My suggestion:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, BEMCheckBoxDelegate {
//...
var checkboxesState: [Int: Bool] = [:]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = mainTableView.dequeueReusableCell(withIdentifier: "productCell", for: indexPath) as! ProductCell
cell.checkBox.delegate = self
cell.checkBox.tag = indexPath.row
if let isOn = checkboxesState[indexPath.row] {
if isOn {
cell.checkBox.on = true
} else {
cell.checkBox.on = false
}
} else {
cell.checkBox.on = false
}
//... other code
return cell
}
func didTap(_ checkBox: BEMCheckBox) {
checkboxesState.updateValue(checkBox.on, forKey: checkBox.tag)
}
//...
}

pass data in tableViewCell to another VC failed

I know it's kinda frequently ask question, but I did some research and none of the solutions work.
so here's my controller with table view
class MainPage: UIViewController, UITableViewDelegate, UITableViewDataSource, YoubikeManagerDelegate {
#IBOutlet weak var tableView: UITableView!
let mainPageCell = MainPageCell()
let mapPage = MapViewController()
var stations: [Station] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "infoCell") as! MainPageCell
cell.stationNameLabel.text = stations[indexPath.row].name
cell.stationLocationLabel.text = stations[indexPath.row].address
cell.numberOfRemainingBikeLabel.text = stations[indexPath.row].numberOfRemainingBikes
cell.printer = stations[indexPath.row].name
cell.mapBtn.tag = indexPath.row
cell.mapBtn.addTarget(self, action: #selector(moveToMapPage), for: .touchUpInside)
return cell
}
func moveToMapPage(sender: UIButton) {
self.performSegue(withIdentifier: "toMap", sender: self)
let nameToPass = stations[sender.tag].name
mapPage.stationName = nameToPass
}
}
there is a UIButton in my tableView cell
class MainPageCell: UITableViewCell {
var printer: String!
let mapPage = MapViewController()
#IBOutlet weak var stationNameLabel: UILabel!
#IBOutlet weak var mapBtn: UIButton!
#IBOutlet weak var stationLocationLabel: UILabel!
#IBOutlet weak var numberOfRemainingBikeLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
mapBtn.addTarget(self, action: #selector(mapBtnTapped), for: .touchUpInside)
}
func mapBtnTapped (sender: UIButton) {
print (printer)
}
}
and this is my other vc
class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var stationName: String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.title = stationName
}
}
I will elaborate my problem that i am now facing here !
the thing I want to do is when I tap the button in tableView cell, I want to go to MapViewController and make the title of this vc "the station name" in the same cell.
so in VC with tableView, in cellforRowAt function I called addTarget.
with moveToMapPage function
but when I tapped the button and goes to MapView VC, the stationName is still nil
I have no clue what goes wrong,
any hints are appreciated
mapPage is not the instance that you are navigating to, so you're setting a variable on an unrelated controller.
You need to use prepare(for segue... if you want to get a link to the new controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
// if you have multiple segues, you can set up different actions for each
if segue.identifier == "segueToMap"
{
let mapPage : MapViewController = segue.destination as! MapViewController
let mapPage : MapViewController = segue.destination as! MapViewController
mapPage.stationName = nameToPass
mapPage.delegate = self // if required
}
}
use navigation controller
let vc = self.storyboard?.instantiateViewController(withIdentifier:"toMap") as! toMapViewController
vc.stationNam = stations[sender.tag].name
self.navigationController?.pushViewController(vc, animated: true)

Table Cells not loading on scroll

I am creating a messaging page in my app and I am having some serious trouble. When the page first loads it looks fine but when you start to scroll down everything gets all messed up. New cells do not load and then when I scroll back up most cells are gone except for normally 1 that shows up and it is in the wrong place.
There is some Async action going on. When the message is sent it sends to the server and then when it returns I reload the data and scroll to that newly entered message at the bottom. This is very buggy as well. Most of the time when sending the message the new cell does not show up. Then after sending 1 or 2 more then will all show up.
Here is my ViewController that is also my Data source and Delegate
import UIKit
class MessageDetailController: UIViewController,UITableViewDataSource,UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var lblMessageText: UITextField!
#IBOutlet weak var sendViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var messagesTable: UITableView!
#IBOutlet weak var sendMsgView: UIView!
#IBOutlet weak var sendMessageView: UIView!
var messageDetailArray = [Message]()
override func viewDidLoad() {
super.viewDidLoad()
self.lblMessageText.delegate = self
self.view.bringSubviewToFront(self.sendMessageView)
self.messagesTable.separatorStyle = UITableViewCellSeparatorStyle.None
self.messagesTable.dataSource = self
self.messagesTable.delegate = self
self.messagesTable.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messageDetailArray.count
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
_ = MessageService().sendMessage("James", message: lblMessageText.text!, callback: loadMessages)
lblMessageText.text = ""
return true
}
func loadMessages(jsonData:NSArray){
dispatch_async(dispatch_get_main_queue(),{
var messages = [Message]()
for messageTest in jsonData {
if let message = messageTest as? NSDictionary {
messages.append(Message(messageType:Message.QA.A, from:message["user_display_label"] as! String, messageText:message["message"] as! String, timeSent:NSDate()))
}
}
self.messageDetailArray = messages;
self.messagesTable.reloadData()
self.messagesTable.scrollToRowAtIndexPath(NSIndexPath(forRow: self.messageDetailArray.count - 1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)
})
}
#IBAction func endedEditing(sender: AnyObject) {
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.2, animations: {
self.sendViewHeightConstraint.constant = 40
self.view.layoutIfNeeded()
}, completion: nil)
}
#IBAction func startedEditing(sender: AnyObject) {
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.2, animations: {
self.sendViewHeightConstraint.constant = 295
self.view.layoutIfNeeded()
}, completion: nil)
}
#IBAction func sendMessage(sender: AnyObject) {
_ = MessageService().sendMessage("James", message: lblMessageText.text!, callback: loadMessages)
lblMessageText.text = ""
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let thisMsg = messageDetailArray[indexPath.row]
let cell: MessageDetailATableViewCell = tableView.dequeueReusableCellWithIdentifier("messageCenterA", forIndexPath: indexPath) as! MessageDetailATableViewCell
cell.msgLabel?.text = thisMsg.messageText;
cell.msgLabel.sizeToFit()
cell.msgLabel.numberOfLines = 0
cell.frame = CGRectMake(0,
0,
self.view.bounds.width,
cell.frame.size.height);
let myInsets = UIEdgeInsetsMake(7, 8, 7, 8)
let bubbleImage = UIImage(named: "balloon_read_right")?.resizableImageWithCapInsets(myInsets)
let bubbleImageView = UIImageView(image: bubbleImage)
bubbleImageView.frame = CGRectMake(0, 0, cell.bubbleView.bounds.width, cell.bubbleView.bounds.height)
cell.bubbleView.addSubview(bubbleImageView)
cell.bubbleView.bringSubviewToFront(cell.msgLabel)
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let messageSize:CGSize = PTSMessagingCell.messageSize(messageDetailArray[indexPath.row].messageText)
return messageSize.height + 2 * PTSMessagingCell.textMarginVertical() + 40
}
}
If you read through that you will notice that I use this class so I will include it for you. It is just the tablecell and does not do much.
import UIKit
class MessageDetailATableViewCell: UITableViewCell {
#IBOutlet weak var bubbleView: UIView!
#IBOutlet weak var msgImageView: UIImageView!
#IBOutlet weak var msgLabel: 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
}
}
I have messed around with this code a lot trying to get it to work and I cont find anyone else who has the same bug. Any help would be greatly appreciated!
Thanks!!
The problem is caused by the fact that you set the frame of your cells. You set the origin of all cells to (0,0). That destroys the layout of the table view. You should never touch the frames of the cells from the outside.
If you want to set the height of your cells you have to do that in the UITableViewDelegate method tableView(_:heightForRowAtIndexPath:). Calculate the cell height there and return it.
You do not have to set the width of your cells, because in a UITableView the UITableViewCells always have the same width as the UITableView.

Resources