JSQMessages Are Being Shown Under Navigation Bar From Show Segue - swift

I have a ChatViewController that extends JSQMessagesViewController.
final class ChatViewController: JSQMessagesViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
title = "Messages"
collectionView?.collectionViewLayout.incomingAvatarViewSize = .zero
collectionView?.collectionViewLayout.outgoingAvatarViewSize = .zero
automaticallyScrollsToMostRecentMessage = true
collectionView?.reloadData()
collectionView?.layoutIfNeeded()
collectionView?.collectionViewLayout.springinessEnabled = true
// REMOVE the attachment button for now
self.inputToolbar.contentView?.leftBarButtonItem = nil
self.inputToolbar.contentView?.textView?.placeHolder = "Compose a message to your driver"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
observeMessages()
}
private func observeMessages() {
messageRef = channelRef!.child("messages")
let messageQuery = messageRef.queryLimited(toLast:25)
newMessageRefHandle = messageQuery.observe(.childAdded, with: { (snapshot) -> Void in
let messageData = snapshot.value as! Dictionary<String, String>
if let id = messageData["senderId"] as String!, let name = messageData["senderName"] as String!, let text = messageData["text"] as String!, text.characters.count > 0 {
self.addMessage(withId: id, name: name, text: text)
self.finishReceivingMessage()
} else {
print("Error! Could not decode message data")
}
})
}
}
This view controller is called by a push segue that has a navigation controller. Within my storyboard, I have a simple segue from a bar item button that pushes to a View Controller with ChatViewController set as the custom class. Within the storyboard, the navigation bar is inferred (shown on top).
However, when I go into ChatViewController, the messages are shown under the navigation bar. Once I click into the message text box, the messages are correctly positioned. How do I fix this so the user does not have to click into the text box? Is there a command I'm missing that correctly lays out the new messages?

This issue has been addressed here and here, actually. You can simply set the top inset to account for the bar's height. Jesse Squires actually made a special member for such cases called topContentAdditionalInset, which you can use like this:
override func viewDidLoad() {
super.viewDidLoad()
self.topContentAdditionalInset = myBarsHeight // in CGFloat
}

Related

how to change UITabBarItem's Title dynamically

I have a TabBarController in my App, and the 2nd item should have title Login or Profile depending on whether the user is logged in or not.
in TabBarController:
import UIKit
class TabNavigationBarVC: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
self.delegate = self
if UserDefaults.standard.bool(forKey: "isUserLoggedIn") {
tabBar.items?[1].title = "Prof"
tabBar.items?[1].image = UIImage(named: "user_male")
} else {
tabBar.items?[1].title = "Log"
tabBar.items?[1].image = UIImage(named: "user_male")
}
}
}
and 2nd TabBarItem is connected with RouterVC:
import UIKit
class RouterViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
let profile = storyboard?.instantiateViewController(withIdentifier: "ComponentUserProfile") as? UserProfileViewController
let login = storyboard?.instantiateViewController(withIdentifier: "ComponentLogin") as? LoginViewController
if UserDefaults.standard.bool(forKey: "isUserLoggedIn") {
//profile?.tabBarItem.title = "prr" - no effect
//profile?.tabBarItem = UITabBarItem(title: "PROFILE", image: UIImage(named: "user_male"), tag: 0) - no effect
viewControllers = [profile] as! [UIViewController]
} else {
//login?.tabBarItem.title = "logg"- no effect
//login?.tabBarItem = UITabBarItem(title: "LOGIN", image: UIImage(named: "user_male"), tag: 0)- no effect
viewControllers = [login] as! [UIViewController]
}
}
}
all works correctly but only when the user launches the app. Later, after the user has logged out, I would like the 2nd title in the tabBar to change from Profile to login, or - when the user is successfully logged in - from Login to Profile. I tried something like commented lines of code in RouterVC, but nothing changed.
And in LoginVC this lines not working too:
override func viewWillAppear(_ animated: Bool) {
self.tabBarItem.title = "loggggg"
}
How can I make this change dynamically?
Maybe I should write extension for TabBarController to track if UserDefaults.standard.bool(forKey: "isUserLoggedIn") is changed and display correct title of the tabbarItem?..
p.s.navigation between views, login/logout works good, the point is only in title
p.p.s - find the answer how get access to the title from view - add self.tabBarController?.tabBar.items?[1].title = "profile", but still looking for some common decision, like tracking UserDefaults key..
you can update the selected Item title using
self.tabBarController?.tabBar.selectedItem?.title = "demo"

Why delegate event is not received swift?

I would like to pass data from EditPostViewController to NewsfeedTableViewController using delegates, but func remove(mediaItem:_) is never called in the adopting class NewsfeedTableViewController. What am I doing wrong?
NewsfeedTableViewController: UITableViewController, EditPostViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//set ourselves as the delegate
let editPostVC = storyboard?.instantiateViewController(withIdentifier: "EditPostViewController") as! EditPostViewController
editPostVC.delegate = self
}
//remove the row so that we can load a new one with the updated data
func remove(mediaItem: Media) {
print("media is received heeeee")
// it does't print anything
}
}
extension NewsfeedTableViewController {
//when edit button is touched, send the corresponding Media to EditPostViewController
func editPost(cell: MediaTableViewCell) {
let editPostVC = storyboard?.instantiateViewController(withIdentifier: "EditPostViewController") as? EditPostViewController
guard let indexPath = tableView.indexPath(for: cell) else {
print("indexpath was not received")
return}
editPostVC?.currentUser = currentUser
editPostVC?.mediaReceived = cell.mediaObject
self.navigationController?.pushViewController(editPostVC!, animated: true)
}
protocol EditPostViewControllerDelegate: class {
func remove(mediaItem: Media)
}
class EditPostViewController: UITableViewController {
weak var delegate: EditPostViewControllerDelegate?
#IBAction func uploadDidTap(_ sender: Any) {
let mediaReceived = Media()
delegate?.remove(mediaItem: mediaReceived)
}
}
The objects instantiating in viewDidLoad(:) and on edit button click event are not the same objects. Make a variable
var editPostVC: EditPostViewController?
instantiate in in viewDidLoad(:) with delegate
editPostVC = storyboard?.instantiateViewController(withIdentifier: "EditPostViewController") as! EditPostViewController
editPostVC.delegate = self
and then present it on click event
navigationController?.pushViewController(editPostVC, animated: true)
or
present(editPostVC, animated: true, completion: nil)
you can pass data from presenter to presented VC before or after presenting the VC.
editPostVC.data = self.data
I suggest having a property in NewsfeedTableViewController
var editPostViewController: EditPostViewController?
and then assigning to that when you instantiate the EditPostViewController.
The idea is that it stops the class being autoreleased when NewsfeedTableViewController.viewDidLoad returns.

swift mac osx NSButton is not responding until a long press

I am having a weird issue with a button. So I have a NSViewController with many subviews in it. When I click a button, a new NSView with click gestures and buttons is added on top. But I can't press any of them, they don't respond unless a click for 2 seconds and then release. I've tried disabling the gestures of the holder but it didn't work. Any suggestions?
Well, some of the rest of us do. In my case, it's for buttons on a view in a sheet, so "many subviews" isn't likely it. My view controller for the sheet is about 100 lines. Still debugging...
At present the VC is as follows. The snp.makeConstraints calls are for SnapKit (from GitHub)
#objc
class ThreadEditSheetViewController: NSViewController {
/// The container for the graphics view
#IBOutlet var sheetView: NSView!
/// The information packet initialized by the invoking view controller
var info: ThreadEditInfo!
/// API
override func viewDidLoad() {
super.viewDidLoad()
}
/// API
override func viewWillAppear() {
guard let gvc = (try? self.bundleLoader(id: "GraphicsViewController")) as? GraphicsViewController else {
fatalUserAlert(error: AppError.UIConstructionFailure, message: "Can't find GraphicsViewController for ThreadEditSheetViewController")}
let gv = gvc.view
self.view.addSubview(gv)
// Spaces in title text move it left to avoid visual overlap with scroll bar. Don't know how to do it with
// constraints given the scrolling view
let done = makeButton(gvc: gvc, title: "done ", action: #selector(doneEditing(_:)))
done.snp.makeConstraints{ (make) in
make.top.equalTo(gv).offset(-5)
make.right.equalTo(gv).offset(-5)
}
let cancel = makeButton(gvc: gvc, title: "cancel", action: #selector(cancelEditing(_:)))
cancel.snp.makeConstraints{ (make) in
make.top.equalTo(gv).offset(-5)
make.left.equalTo(gv).offset(5)
}
self.view.becomeFirstResponder()
super.viewWillAppear()
return
}
func makeButton(gvc: NSViewController, title: String, action: Selector) -> NSButton {
let button = NSButton(title: title, target: self, action: action)
let gv = gvc.view
gv.addSubview(button)
button.backgroundColor = .clear
button.setButtonType(.momentaryChange)
button.isTransparent = true
return button
}
#objc
func doneEditing(_ sender: Any) {
self.dismissViewController(self)
}
#objc
func cancelEditing(_ sender: Any) {
self.dismissViewController(self)
}
}

setup navigation controller View Controller as a CNContactViewController black screen

I have a collection view with some cells representing a contact (their data has a phone number and name) and I am trying to add the contact to the iPhone contacts. I have created a segue from a button called "add contact" that is inside the CollectionViewCell to a navigation controller, and set its identifier as "ADD_CONTACT".
In the storyboard, my segue has a navigation controller with no root view controller.
in prepareToSegue of the view controller that delegates my UICollectionView I wrote this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == ADD_CONTACT {
let dest = segue.destination as! UINavigationController
if let cell = sender as? SBInstructionCell {
if cell.isContact {
let newContact = CNMutableContact()
if let phone = cell.instructionBean?.contactAttachment?.phoneNumber{
newContact.phoneNumbers.append(CNLabeledValue(label: "home", value: CNPhoneNumber(stringValue: phone)))
}
if let name = cell.instructionBean?.contactAttachment?.contactName {
newContact.givenName.append(name)
}
let contactVC = CNContactViewController(forNewContact: newContact)
contactVC.contactStore = CNContactStore()
contactVC.delegate = self
dest.setViewControllers([contactVC], animated: false)
}
}
}
}
this results with a black screen.
How can this be fixed? I want to see the CNContactViewController
Eventually I solved this in a different approach using Closures.
In my UICollectionViewCell
I added this var:
var closureForContact: (()->())? = nil
Now on my button's action in the same cell I have this func:
#IBAction func addContactTapped(_ sender: UIButton) {
if closureForContact != nil{
closureForContact!()
}
}
Which calls the function.
In my CollectionView in cell for item at index path, I set the closure like this:
cell.closureForContact = {
if cell.isContact {
let newContact = CNMutableContact()
if let phone = cell.instructionBean?.contactAttachment?.phoneNumber{
newContact.phoneNumbers.append(CNLabeledValue(label: "home", value: CNPhoneNumber(stringValue: phone)))
}
if let name = cell.instructionBean?.contactAttachment?.contactName {
newContact.givenName.append(name)
}
let contactVC = CNContactViewController(forNewContact: newContact)
contactVC.contactStore = CNContactStore()
contactVC.delegate = self
contactVC.allowsEditing = true
contactVC.allowsActions = true
if let nav = self.navigationController {
nav.navigationBar.isTranslucent = false
nav.pushViewController(contactVC, animated: true)
}
}
}
This worked perfectly. I learned that for navigating from a cell, it is best to use closures.

prepareForSegue works on iOS7 but not on iOS8

I've been struggling with this one for a while. The following code runs fine when build for ios7, but when build for ios8 just crashes. Any of these got deprecated on ios8?
Not sure if I am doing wrong by pushing a segue from a tab bar controller to a navigation controller.
If I comment my var moveVC I can transit to the HobbieFeedViewController, but then I do not pass any values to the next segue.
If I remove the navigation controller from HobbieFeedViewController the segue stops working.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
println("SENDER->\(sender)")
let cell = sender as! HobbiesTableViewCell
var cellTitle = String()
//unwrap safelly
// if let object = cell.textLabel?.text
if let object = cell.cellTitle.text
{
cellTitle = object
println("CELLTITLE->\(cellTitle)")
}
// if not
else
{
println("Could not get cellTitle")
}
// call segue
if (segue.identifier == "hobbieFeed")
{
var selectedRowIndex = self.myTableView.indexPathForSelectedRow()
// var moveVC: HobbieFeedViewController = segue.destinationViewController as! HobbieFeedViewController
//
// moveVC.moveId = selectedRowIndex!.row
// moveVC.selectedHobbie = cellTitle
println("PREPARE ROW->\(selectedRowIndex)")
println("PREPARE HOBBIE->\(cellTitle)")
}
}