Swift Tab Bar goes Black instead of disappearing - swift

When I go to my settings view controller, the tab bar is supposed to go away; however instead of going a way it turns black covering some of the settings page. Here is the image (I covered private info):
Here is my code for the settings controller:
private let reuseIdentifier = "SettingsCell"
class SettingsViewController: UIViewController {
// MARK: - Properties
override func viewDidAppear(_ animated: Bool) {
self.navigationController?.navigationBar.prefersLargeTitles = true
}
var tableView: UITableView!
var userInfoHeader: UserInfoHeader!
var user: User?
// MARK: - Init
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
}
// MARK: - Helper Functions
func configureTableView() {
tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 60
tableView.register(SettingsCell.self, forCellReuseIdentifier: reuseIdentifier)
view.addSubview(tableView)
tableView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 800)
let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 85)
userInfoHeader = UserInfoHeader(frame: frame)
userInfoHeader.usernameLabel.text = "\(user?.firstName ?? "") \(user?.lastName ?? "")"
userInfoHeader.schoolLabel.text = user?.school
if let imageUrl = user?.imageUrl1, let url = URL(string: imageUrl) {
// Nuke.loadImage(with: url, into: self.image1Button)
SDWebImageManager().loadImage(with: url, options: .continueInBackground, progress: nil) { (image, _, _, _, _, _) in
self.userInfoHeader.profileImageView.image = image
}
}
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(goToProfile))
userInfoHeader.addGestureRecognizer(tapRecognizer)
tableView.tableHeaderView = userInfoHeader
tableView.tableFooterView = UIView()
}
#objc fileprivate func goToProfile() {
let userDetailsController = CurrentUserDetailsNoReportController()
let myBackButton = UIBarButtonItem()
myBackButton.title = " "
navigationItem.backBarButtonItem = myBackButton
userDetailsController.cardViewModel = user?.toCardViewModel()
navigationController?.pushViewController(userDetailsController, animated: true)
}
func configureUI() {
configureTableView()
// navigationController?.navigationBar.isTranslucent = false
navigationItem.title = "Settings"
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "icons8-chevron-left-30").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleBack))
}
#objc fileprivate func handleBack() {
dismiss(animated: true)
}
#objc fileprivate func handleSwitchNotification(sender: UISwitch) {
if sender.isOn {
showSettingsAlert(sender: sender)
}
else {
showSettingsAlert(sender: sender)
}
}
#objc fileprivate func handleSwitchLocation(sender: UISwitch) {
if sender.isOn {
showSettingsAlert2(sender: sender)
}
else {
showSettingsAlert2(sender: sender)
}
}
private func showSettingsAlert(sender: UISwitch) {
let alert = UIAlertController(title: "Notifications Permissions", message: "Change your notifications permissions?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Open Settings", style: .default) { action in
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
sender.isOn = false
return
})
present(alert, animated: true)
}
private func showSettingsAlert2(sender: UISwitch) {
let alert = UIAlertController(title: "Location Permissions", message: "Change your location permissions?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Open Settings", style: .default) { action in
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
sender.isOn = false
return
})
present(alert, animated: true)
}
}
extension SettingsViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return SettingsSection.allCases.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let section = SettingsSection(rawValue: section) else {return 0}
switch section {
case .Social:
return SocialOptions.allCases.count
case .Communications:
return CommunicationOptions.allCases.count
case .About:
return AboutOptions.allCases.count
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = .white
let title = UILabel()
title.font = UIFont.boldSystemFont(ofSize: 22)
title.textColor = #colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)
title.text = SettingsSection(rawValue: section)?.description
view.addSubview(title)
title.anchor(top: view.topAnchor, leading: view.leadingAnchor, bottom: view.bottomAnchor, trailing: nil, padding: .init(top: 2, left: 10, bottom: 2, right: 0))
return view
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! SettingsCell
guard let section = SettingsSection(rawValue: indexPath.section) else {return UITableViewCell()}
switch section {
case .Social:
let social = SocialOptions(rawValue: indexPath.row)
cell.sectionType = social
case .Communications:
let communications = CommunicationOptions(rawValue: indexPath.row)
cell.sectionType = communications
switch communications {
case .location:
switch CLLocationManager.authorizationStatus() {
case .notDetermined, .restricted, .denied:
cell.switchControl.isOn = false
cell.switchControl.addTarget(self, action: #selector(handleSwitchLocation), for: .valueChanged)
default:
cell.switchControl.isOn = true
cell.switchControl.addTarget(self, action: #selector(handleSwitchLocation), for: .valueChanged)
}
default:
let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
if isRegisteredForRemoteNotifications {
cell.switchControl.isOn = true
cell.switchControl.addTarget(self, action: #selector(handleSwitchNotification), for: .valueChanged)
} else {
cell.switchControl.isOn = false
cell.switchControl.addTarget(self, action: #selector(handleSwitchNotification), for: .valueChanged)
}
}
case .About:
let about = AboutOptions(rawValue: indexPath.row)
cell.sectionType = about
}
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let section = SettingsSection(rawValue: indexPath.section) else {return}
switch section {
case .Social:
let social = SocialOptions(rawValue: indexPath.row)
if social?.description == "Edit Profile" {
let settingsController = NiceEditProfile()
settingsController.user = user
let myBackButton = UIBarButtonItem()
myBackButton.title = " "
self.navigationItem.backBarButtonItem = myBackButton
self.navigationController?.pushViewController(settingsController, animated: true)
}
else if social?.description == "Logout" {
let firebaseAuth = Auth.auth()
let loginViewController = LoginViewController()
let navController = UINavigationController(rootViewController: loginViewController)
navController.modalPresentationStyle = .fullScreen
do {
try firebaseAuth.signOut()
} catch { }
present(navController, animated: true)
}
case .Communications:
let communications = CommunicationOptions(rawValue: indexPath.row)
case .About:
let about = AboutOptions(rawValue: indexPath.row)
if about?.description == "Privacy" {
let privacyCon = PrivacyController()
self.navigationController?.pushViewController(privacyCon, animated: true)
}
else if about?.description == "Terms of Use" {
let termsController = TermsViewController()
self.navigationController?.pushViewController(termsController, animated: true)
}
}
}
}
Here is my transition to the Settings Controller (this controller has a tab bar)
#objc func handleSettings() {
let settingsController = ViewController()
//settingsController.delegate = self
settingsController.user = user
let navController = UINavigationController(rootViewController: settingsController)
navController.modalPresentationStyle = .fullScreen
present(navController, animated: true)
}
Hiding the tab bar doesn't work. This seemed to only happen after like iOS 13.5 What could be going on?

Just select the hide bottom bar on push in your view controller in st:

Related

How to deselect all buttons when one of them is selected

I have a tableview and inside one of my rows I have buttons. I want to deselect all buttons when one is selected and change the background color to the red when button is selected. I saw lots of example but I couldn't do it in my own code.
func configure(_ modelArray : [UnitAndColors], colorTitle:String, packingTitle:String) {
self.unitStackView.removeAllArrangedSubviews()
self.arrayUnitsAndColor?.removeAll()
self.arrayUnitsAndColor = modelArray
let filteredArray = self.arrayUnitsAndColor?.unique { $0.colorDesc }
var i : Int = 0
filteredArray?.forEach { units in
let vw = self.createUnits(units, tag: i)
self.unitStackView.addArrangedSubview(vw)
vw.snp.makeConstraints {
$0.size.equalTo(40.0)
}
i = i + 1
}
}
func createUnits(_ model : UnitAndColors, tag: Int) -> UIStackView {
let stackViewMain = UIStackView()
stackViewMain.axis = .horizontal
stackViewMain.spacing = 4
let labelPackingUnit = UIButton()
labelPackingUnit.backgroundColor = Colors().colorWhite
labelPackingUnit.tag = tag
labelPackingUnit.setTitleColor(Colors().colorRed, for: .normal)
labelPackingUnit.addTarget(self, action: #selector(selectUnit(_:)), for: .touchUpInside)
labelPackingUnit.titleLabel?.font = UIFont.fontBold16
labelPackingUnit.contentHorizontalAlignment = .center
labelPackingUnit.setBorder(width: 1, color: Colors().colorRed)
labelPackingUnit.setCornerRound(value: 20.0)
labelPackingUnit.setTitle(model.unitDesc, for: .normal)
stackViewMain.addArrangedSubview(labelPackingUnit)
labelPackingUnit.snp.makeConstraints {
$0.size.equalTo(40)
}
return stackViewMain
}
#objc private func selectButton(_ sender : UIButton) {
let tag : Int = sender.tag
guard let model : UnitAndColors = self.arrayUnitsAndColor?.filter({ $0.colorDesc == selectedColorName })[tag] else { return }
selectedUnit = model.unitDesc ?? ""
delegate?.changePrice(selectedPrice: model.modelPrice, arrayUnitsAndColor ?? [])
}
It's pretty frustrating to understand what's happening in your code, but I think that's what you're looking for:
#objc private func selectButton(_ sender : UIButton) {
let tag : Int = sender.tag
guard let model: UnitAndColors = self.arrayUnitsAndColor?.filter({ $0.colorDesc == selectedColorName })[tag] else { return }
selectedUnit = model.unitDesc ?? ""
for stackView in unitStackView.arrangedSubviews as? [UIStackView] ?? [] {
guard let button = stackView.arrangedSubviews.first as? UIButton else {
return
}
button.isSelected = false
button.backgroundColor = .clear
}
selectedUnit.isSelected = true
selectedUnit.backgroundColor = .red
delegate?.changePrice(selectedPrice: model.modelPrice, arrayUnitsAndColor ?? [])
}
var selectedRows: [Int] = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.allowMultipleSelection = true
}
func selectAll(){
for i in 0..<totalRows.count{
selectedRows.append(i)
let indexPath = IndexPath(row: i, section: 0)
self.tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
}
func deselectAll(){
for i in 0..<totalRows.count{
selectedRows.append(i)
let indexPath = IndexPath(row: i, section: 0)
self.tableView.deselectRow(at: indexPath, animated: true)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
selectedRows.append(indexPath.row)
print(selectedRows)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = selectedRows.firstIndex(of: indexPath.row) {
selectedRows.remove(at: index)
}
print(selectedRows)
if selectedRows.isEmpty{
}
}
}
class TableViewCell: UITableViewCell{
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if (selected) {
print("selected")
contentView.backgroundColor = .red
} else {
contentView.backgroundColor = .red
}
}
}

how to call/show action sheet for taking or choose photo from gallery on Main VC after tapped on TableViewCell Image(swift)

Here I Try MVVM pattern to achieve TableView but for showing alert I face problems , it compiles successfully but not showing alert.
[Result] on the tapped on profile pic which is in tableview cell, I want to show alert
TableViewCell
import Foundation
import UIKit
class ParentsImageCell : UITableViewCell {
weak var myVC : ProfileOfParentsDetailsViewController?
var parentProfileVC = ProfileOfParentsDetailsViewController()
#IBOutlet weak var imageProfile : UIImageView!
var items : ParentProfileViewModelItem? {
didSet {
guard let items = items as? ParentProfileViewModelProfileItem else {
return
}
imageProfile?.image = UIImage(named: items.profileImg)
}
}
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
override func awakeFromNib() {
super.awakeFromNib()
imageProfile?.layer.cornerRadius = 62
imageProfile?.clipsToBounds = true
imageProfile?.contentMode = .scaleAspectFill
imageProfile?.backgroundColor = UIColor.lightGray
//Add Tapped Gesture
imageProfile.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(
target: self,
action: #selector(didTappedChangeProfilePic))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
imageProfile.addGestureRecognizer(gesture)
}
#objc private func didTappedChangeProfilePic(){
print("tapped on imageView")
presentPhotoActionSheet()
}
override func prepareForReuse() {
super.prepareForReuse()
imageProfile?.image = nil
}
}
extension ParentsImageCell : UIImagePickerControllerDelegate ,UINavigationControllerDelegate {
func presentPhotoActionSheet(){
let actionSheet = UIAlertController(title: "Profile Picture", message: "How would you write to select a picture", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: {[weak self] _ in
self?.presentCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Choose Photo", style: .default, handler: { [weak self]_ in
self?.presentPhotoPicker()
}))
myVC?.present(actionSheet , animated: true)
}
func presentCamera(){
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func presentPhotoPicker(){
let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:
[UIImagePickerController.InfoKey : Any]) {
guard let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
self.imageProfile.image = selectedImage
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
ViewModel
class ParentProfileViewModel: NSObject {
var items = [ParentProfileViewModelItem]()
var reloadSections: ((_ section: Int) -> Void)?
override init() {
super.init()
guard let data = dataFromFile("ServerData"),
let profile = Profile(data: data) else {
return
}
// initialization code will go here
if let profile = profile.pictureUrl {
let profileItem = ParentProfileViewModelProfileItem(profileImg: profile)
items.append(profileItem)
}
if let name = profile.fullName {
let nameItem = ParentProfileViewModelNameItem(name: name)
items.append(nameItem)
}
if let email = profile.email {
let emailItem = ParentProfileViewModelEmailItem(email: email)
items.append(emailItem)
}
let coach = profile.coach
if !coach.isEmpty {
let coachItem = ParentProfileViewModelCoachItem(coach: coach)
items.append(coachItem)
}
let candidate = profile.candidate
if !candidate.isEmpty {
let candidateItem = ParentProfileViewModelCandidateItem(candidate: candidate)
items.append(candidateItem)
}
}
}
//MARK:- TableviewDatasource & Delegates
extension ParentProfileViewModel: UITableViewDataSource {
//Number of section
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
//Number of RowInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let item = items[section]
guard item.isCollapsible else {
return item.rowCount
}
if item.isCollapsed {
return 0
} else {
return item.rowCount
}
}
//Cell for row at indexpath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// we will configure the cells here
let item = items[indexPath.section]
switch item.type {
case .profileImg:
let vc = ProfileOfParentsDetailsViewController()
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsImageCell.identifier, for: indexPath) as? ParentsImageCell {
cell.items = item
cell.myVC = vc
return cell
}
case .fullName:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsFulNameCell.identifier, for: indexPath) as? ParentsFulNameCell {
cell.items = item
return cell
}
case .email:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsEmailCell.identifier, for: indexPath) as? ParentsEmailCell {
cell.items = item
return cell
}
case .candidate:
if let item = item as? ParentProfileViewModelCandidateItem, let cell = tableView.dequeueReusableCell(withIdentifier: CandidatCell.identifier, for: indexPath) as? CandidatCell {
let candidate = item.candidate[indexPath.row]
cell.item = candidate
return cell
}
case .coach:
if let item = item as? ParentProfileViewModelCoachItem, let cell = tableView.dequeueReusableCell(withIdentifier: ParentCoachCell.identifier, for: indexPath) as? ParentCoachCell {
cell.item = item.coach[indexPath.row]
return cell
}
}
return UITableViewCell()
}
}
ViewController
import Foundation
import UIKit
class ProfileOfParentsDetailsViewController: UIViewController {
let viewModel = ParentProfileViewModel()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
viewModel.reloadSections = { [weak self] (section: Int) in
self?.tableView?.beginUpdates()
self?.tableView?.reloadSections([section], with: .fade)
self?.tableView?.endUpdates()
}
tableView?.dataSource = viewModel
tableView.delegate = viewModel
tableView?.estimatedRowHeight = 250
tableView?.rowHeight = UITableView.automaticDimension
tableView?.register(ParentsImageCell.nib, forCellReuseIdentifier: ParentsImageCell.identifier)
tableView?.register(ParentsEmailCell.nib, forCellReuseIdentifier: ParentsEmailCell.identifier)
tableView?.register(ParentsFulNameCell.nib, forCellReuseIdentifier: ParentsFulNameCell.identifier)
tableView?.register(CandidatCell.nib, forCellReuseIdentifier: CandidatCell.identifier)
tableView?.register(ParentCoachCell.nib, forCellReuseIdentifier: ParentCoachCell.identifier)
tableView?.register(ParentsHeaderView.nib, forHeaderFooterViewReuseIdentifier: ParentsHeaderView.identifier)
}
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM

when fetch images from firebase, tableView not scroll to row Swift

I begin to create a chat app and want it to scroll to bottom every time I enter text or image. For text, the code is perfect to execute scrollToRow after tableView reload. However, the image always shows under the keyboard, I tried many ways to solve this issue but still get the same result.
image does not show above the keyboard
image is under keyboard
class ChatTableViewController: UITableViewController, UITextViewDelegate {
private let reuseIdentifier = "reuseIdentifier"
private let chatImageIdentifier = "chatImageCell"
var sSection = 0
var rRow = 0
var commentRef: DatabaseReference!
var firebaseHandle: DatabaseHandle?
var dataSnapshot: DataSnapshot?
let uid = FirebaseHelper.shared.getUserId()
let username = FirebaseHelper.shared.getUsername()
let email = FirebaseHelper.shared.getUserEmail()
var comments = [Comment]()
var intArray: [Int]? {
didSet{
guard let intArray = intArray else {return}
self.sSection = intArray[0]
self.rRow = intArray[1]
commentRef = FirebaseHelper.shared.getCommentRef(firebaseRef: .comments, section: sSection, row: rRow)
comments = []
tableView.reloadData()
observeCommentsFromFirebase {
}
setUpKeyboardObserver()
}
}
func observeCommentsFromFirebase(completion: #escaping (()->Void)) {
firebaseHandle = commentRef.queryOrdered(byChild: "timeStamp").observe(.childAdded, with: { (snapshot) in
self.dataSnapshot = snapshot
if let dictionary = snapshot.value as? [String: Any]{
let comment = Comment(dictionary: dictionary)
self.comments.append(comment)
DispatchQueue.main.async {
self.tableView.reloadData()
self.handleKeyboardDidShow()
completion()
}
}
}, withCancel: nil)
}
func setUpKeyboardObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)
}
#objc func handleKeyboardDidShow() {
if comments.count > 0 {
let indexPath = IndexPath(row: comments.count - 1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(ChatTableViewCell.self, forCellReuseIdentifier: reuseIdentifier)
tableView.register(ChatImageCell.self, forCellReuseIdentifier: chatImageIdentifier)
tableView.separatorStyle = .none
tableView.allowsSelection = false
tableView.backgroundColor = grayColor
tableView.keyboardDismissMode = .interactive
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(handleDismiss))
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
tableView.alwaysBounceVertical = true
}
#objc func handleDismiss() {
if let firebaseHandle = firebaseHandle {
self.commentRef.removeObserver(withHandle: firebaseHandle)
}
NotificationCenter.default.removeObserver(self)
self.dismiss(animated: true, completion: nil)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let comment = comments[indexPath.row]
if comment.imageUrl != "" {
let cell = tableView.dequeueReusableCell(withIdentifier: chatImageIdentifier, for: indexPath) as! ChatImageCell
if comments.count != 0 {
cell.comment = comment
}
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! ChatTableViewCell
if comments.count != 0 {
cell.comment = comment
}
return cell
}
}
#objc func uploadImage() {
showImageActionSheet()
}
extension ChatTableViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func showImageActionSheet() {
// declaring action sheet
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// declaring camera button
let camera = UIAlertAction(title: "Camera", style: .default) { (action) in
// if there is camera, then execute
if UIImagePickerController.isSourceTypeAvailable(.camera) {
self.showPicker(with: .camera)
}
}
// declaring library button
let library = UIAlertAction(title: "Library", style: .default) { (action) in
// if there is library, then execute
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
self.showPicker(with: .photoLibrary)
}
}
// declaring cancel button
let cancel = UIAlertAction(title: "Cancel", style: .cancel)
// add action to the sheet
alertController.addAction(camera)
alertController.addAction(library)
alertController.addAction(cancel)
// present action sheet to the user
self.present(alertController, animated: true, completion: nil)
}
func showPicker(with source: UIImagePickerController.SourceType) {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
picker.sourceType = source
self.present(picker, animated: true, completion: nil)
}
// fetch selected image from info with key
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
var selectedImage: UIImage?
// declaring editedImage
if let editedImage = info[.editedImage] as? UIImage {
selectedImage = editedImage
} else if let originalImage = info[.originalImage] as? UIImage {
selectedImage = originalImage
}
self.dismiss(animated: true, completion: nil)
FirebaseHelper.shared.uploadImage(image: selectedImage, firebaseRef: .comment_images, viewController: self) {
urlString in
let timeStamp = Helper.shared.dateOfCurrentTimeZone()
let childKey = FirebaseHelper.shared.getChildKey()
let profileImageUrlString: String
if let profileImageUrl = FirebaseHelper.shared.getPhotUrl() {
profileImageUrlString = profileImageUrl.absoluteString
} else {
profileImageUrlString = ""
}
let values: [String: Any] = ["text": "", "uid": self.uid, "timeStamp": timeStamp, "childkey": childKey, "username": self.username, "email": self.email ?? "", "imageUrl": urlString, "profileImageUrl": profileImageUrlString]
self.commentRef.child(childKey).updateChildValues(values, withCompletionBlock: { (error, dataRef) in
if error != nil {
Helper.shared.alertController(title: "Error", message: error!.localizedDescription, viewController: self)
return
}
})
}
}
}

How do I call a function when UINavigationController UINavigationTabBar Item is tapped?

I want to be able to present a UIAlertAction when the user taps a UINavigationController UINavigationTabBar Item to remind them they need to enter some information before they can proceed to the next UIViewController that would otherwise be called from such a tap. The code that is called from the tap would check if an array is empty, if so, then display the UIAlertAction.
I've tried to create an instance of the UINavigationController's UINavigationTabBar item, to try and detect it being pushed, but this is messy as the UIViewController to be called has already been setup as a segue when the UINavigationTabBar item is tapped.
Do I need to backtrack and remove the existing segue and create the transition in a different way that allows my desired array checking code to be callable on the user's tap?
If you're pushing the next view controller using a segue, you would intercept that segue, do all your checks, and then inform the segue whether to go on or cancel the pushing. Basically in your view controller:
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "NextViewControllerID" {
var isValid = true
// example of validation, do your stuff here
if myTextField.text?.count ?? 0 < 5 {
isValid = false
}
return isValid
}
// otherwise delegates the superclass
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
}
import UIKit
class WordsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
// write button selector method to check if there are any words.
var words = [String]()
var languages = [String]()
var chosenLanguage = String()
var textField: UITextField?
let wordString = "Words"
override func viewDidLoad() {
super.viewDidLoad()
chosenLanguageLoad()
loadInitialValues()
//let titleAttributes = [NSAttributedString.Key.font: UIFont(name: "AmericanTypewriter", size: 22)!]
//navigationController?.navigationBar.titleTextAttributes = titleAttributes
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(test))
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addNewWord))
print("languages array is: \(languages)")
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "Words" {
print("inside identifier == words if")
var isValid = true
if languages.isEmpty == true {
print("languages is empty is true")
isValid = false
}
return isValid
}
// otherwise delegates the superclass
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
}
#objc func test() {
if words.isEmpty == true {
let ac = UIAlertController(title: "Add Some Words", message: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default)
ac.addAction(okAction)
present(ac, animated: true)
} else {
self.performSegue(withIdentifier: "Test", sender: self)
}
}
func chosenLanguageLoad() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
if let loadChosenLanguage = defaults.object(forKey: "languageChosen") as? String {
chosenLanguage = loadChosenLanguage
print("Chosen language is: \(chosenLanguage)")
}
}
}
#objc func loadInitialValues() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
words.removeAll()
tableView.reloadData()
print("Words array after clear all: \(words)")
print("chosenLanguage in loadInitialValues: \(chosenLanguage)")
print("\(chosenLanguage)\(wordString)")
if var savedWords = defaults.object(forKey: "\(chosenLanguage)\(wordString)") as? [String] {
print("savedWords array is: \(savedWords)")
words = savedWords
savedWords.removeAll()
print("savedWords array after clear all: \(savedWords)")
} else {
saveInitialWordValues(to: defaults)
}
print("Words is : \(words)")
print("Number of words: \(words.count)")
tableView.reloadData()
}
}
func saveInitialWordValues(to defaults: UserDefaults) {
switch chosenLanguage {
case "german":
words.append("Bear::Baissespekulant::0")
words.append("Camel::Kamel::0")
words.append("Cow::Rind::0")
words.append("Fox::Fuchs::0")
words.append("Goat::Geiß::0")
words.append("Monkey::Affe::0")
words.append("Pig::Schwein::0")
words.append("Rabbit::Karnickel::0")
words.append("Sheep::Schaf::0")
//words.append("::")
print(words)
defaults.set(words, forKey: "germanWords")
print("At end of saveInitialGermanValues")
case "french":
words.append("Bear::l'ours::0")
words.append("Camel::le chameau::0")
words.append("Cow::la vache::0")
words.append("Fox::le renard::0")
words.append("Goat::la chèvre::0")
words.append("Monkey::le singe::0")
words.append("Pig::le cochon::0")
words.append("Rabbit::le lapin::0")
words.append("Sheep::le mouton::0")
//words.append("::")
print(words)
defaults.set(words, forKey: "frenchWords")
print("At end of saveInitialFrenchValues")
default:
break
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
chosenLanguageLoad()
loadInitialValues()
}
override func viewDidAppear(_ animated: Bool) {
navigationController?.setToolbarHidden(true, animated: false)
super.viewDidAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
#objc func addNewWord() {
// create our alert controller
let ac = UIAlertController(title: "Add new word", message: nil, preferredStyle: .alert)
// add two text fields, one for English and one for French or German.
ac.addTextField { textField in
textField.placeholder = "English"
}
ac.addTextField { (textField) in
textField.placeholder = "\(self.chosenLanguage.capitalized)"
}
// create an "Add Word" button that submits the user's input
let submitAction = UIAlertAction(title: "Add Word", style: .default) { [unowned self, ac] (action: UIAlertAction!) in
// pull out the English and French words, or an empty string if there was a problem
let firstWord = ac.textFields?[0].text ?? ""
let secondWord = ac.textFields?[1].text ?? ""
let zeroTimesWrong = "0"
// submit the English and French word to the insertFlashcard() method
self.insertFlashcard(first: firstWord, second: secondWord, third: zeroTimesWrong)
}
// add the submit action, plus a cancel button
ac.addAction(submitAction)
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
// present the alert controller to the user
present(ac, animated: true)
}
func insertFlashcard(first: String, second: String, third: String) {
guard first.count > 0 && second.count > 0 else { return }
let newIndexPath = IndexPath(row: words.count, section: 0)
words.append("\(first)::\(second)::\(third)")
tableView.insertRows(at: [newIndexPath], with: .automatic)
saveWords()
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .default, title: "Delete") { action, indexPath in
self.words.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .left)
tableView.endUpdates()
// delete item at indexPath
}
let edit = UITableViewRowAction(style: .normal, title: "Edit") { (action, indexPath) in
let ac = UIAlertController(title: "Edit word", message: nil, preferredStyle: .alert)
// add two text fields, one for English and one for French
ac.addTextField { textField in
let word = self.words[indexPath.row]
let split = word.components(separatedBy: "::")
let englishWord = split[0]
textField.placeholder = "\(englishWord)"
}
ac.addTextField { (textField) in
let word = self.words[indexPath.row]
let split = word.components(separatedBy: "::")
let foreignWord = split[1]
textField.placeholder = "\(foreignWord)"
}
// create an "Add Word" button that submits the user's input
let submitAction = UIAlertAction(title: "Edit Word", style: .default) { [unowned self, ac] (action: UIAlertAction!) in
// pull out the English and French words, or an empty string if there was a problem
let firstWord = ac.textFields?[0].text ?? ""
let secondWord = ac.textFields?[1].text ?? ""
guard firstWord.count > 0 && secondWord.count > 0 else { return }
// edit item at indexPath
self.words.remove(at: indexPath.row)
self.words.insert("\(firstWord)::\(secondWord)", at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.insertRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
self.saveWords()
}
// add the submit action, plus a cancel button
ac.addAction(submitAction)
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
// present the alert controller to the user
self.present(ac, animated: true)
}
edit.backgroundColor = UIColor.blue
return [delete, edit]
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return words.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//print("In cellForRowAt function")
let cell = tableView.dequeueReusableCell(withIdentifier: "Word", for: indexPath)
let word = words[indexPath.row]
if word != "::" {
let split = word.components(separatedBy: "::")
print(split[0])
cell.textLabel?.text = split[0]
cell.detailTextLabel?.text = ""
//print(cell)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) {
if cell.detailTextLabel?.text == "" {
let word = words[indexPath.row]
let split = word.components(separatedBy: "::")
cell.detailTextLabel?.text = split[1]
} else {
cell.detailTextLabel?.text = ""
}
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
words.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
saveWords()
}
func saveWords() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
defaults.set(words, forKey: "\(chosenLanguage)\(wordString)")
}
}
}

Read More and Read Less in UITextView : Ilya Puchka readMoreTextView not working

I have tested Ilya Puchka's readMoreTextView pod and on static text inside the project it works. but I am trying it with text that comes from a back end api.
Issue : the cell will not expand fully to reveal all the text. It will reveal about half of the height of the next line...meaning, if the text is 14 pix high, then the READ MORE option will only display only the top half of the text..
If I disable readmoretext options, and use the textView as a plain textView, then textview will display the entire text,... until I scroll.
when I scroll, I lose about 12 lines of text in one messages.
Is a textView just bad for lone singe strings rom a backend?
so this tells me its a tableView.reloadData() or a GCD issue. But I am lost, I don't know where to try. test either option.
I am mixing two pods, AzureBot and ReadMoreTextView. the string / text comes from the azure bot and then gets loaded into the textview.
I now realize I suck at such a simple task.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let message = self.messages[indexPath.row]
let sending = message.from?.id == BotClient.shared.currentUser.id
switch sending {
case true:
let cell = tableView.dequeueReusableCell(withIdentifier: "UserTableViewCell", for: indexPath) as! UserTableViewCell
cell.transform = tableView.transform
cell.messageLabel.text = message.text
return cell
case false:
let cell = tableView.dequeueReusableCell(withIdentifier: "BotTableCell", for: indexPath) as! BotTableCell
cell.botMessage.text = message.text
cell.botMessage.shouldTrim = !expandedCells.contains(indexPath.row)
cell.botMessage.setNeedsUpdateTrim()
cell.botMessage.layoutIfNeeded()
cell.transform = tableView.transform
return cell
}
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "BotTableCell", for: indexPath) as! BotTableCell
cell.botMessage.onSizeChange = { [unowned tableView, unowned self] r in
let point = tableView.convert(r.bounds.origin, from: r)
guard let indexPath = tableView.indexPathForRow(at: point) else { return }
if r.shouldTrim {
self.expandedCells.remove(indexPath.row)
print("REMOVE CALLED")
} else {
self.expandedCells.insert(indexPath.row)
print("INSERT CALLED")
}
tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "BotTableCell", for: indexPath) as! BotTableCell
cell.botMessage.shouldTrim = !cell.botMessage.shouldTrim
print("SHOULD TRIM CALLED")
cell.backgroundColor = .clear
}
}
Here is my TableViewController Class :
class TMNEMessageViewController: UITableViewController, UITextViewDelegate {
#IBOutlet weak var navBar: UINavigationItem!
private var observer: AnyObject?
private var needsUpdateViewOnAppearance = true
#IBOutlet var messageBar: MessageBar!
public override var inputAccessoryView: UIView? {return messageBar}
public override var canBecomeFirstResponder: Bool {return true}
private var isVisible: Bool {return isViewLoaded && view.window != nil}
var messages: SortedArray<Activity> { return BotClient.shared.messages }
var expandedCells = Set<Int>()
let readMoreTextAttributes: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.foregroundColor: UIColor.red,
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 16)
]
let readLessTextAttributes = [
NSAttributedString.Key.foregroundColor: UIColor.red,
NSAttributedString.Key.font: UIFont.italicSystemFont(ofSize: 16)
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 300
tableView.rowHeight = UITableView.automaticDimension
registerCells()
setupObservers()
tableView.transform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: 0) //flip tableview
BotClient.shared.start { r in
if let _ = r.resource {
print(r.printResponseData())
} else if let error = r.error {
print("ERROR: " + error.localizedDescription)
self.displayError(error.localizedDescription)
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if needsUpdateViewOnAppearance {
tableView.reloadData()
needsUpdateViewOnAppearance = false
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
becomeFirstResponder()
}
public override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if tableView.contentInset == UIEdgeInsets.zero {
updateContentInset(inset: getContentInset())
}
// tableView.reloadData()
}
func registerCells() {
self.tableView.register(UINib(nibName: "BotTableCell", bundle: nil), forCellReuseIdentifier: "BotTableCell")
self.tableView.register(UINib(nibName: "UserTableViewCell", bundle: nil), forCellReuseIdentifier: "UserTableViewCell")
}
fileprivate func setupObservers() {
// Keyboard Notifications
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: UIResponder.keyboardDidChangeFrameNotification, object: nil)
// BotClient Notifications
NotificationCenter.default.addObserver(self, selector: #selector(handleMessageAdded(notification:)), name: .BotClientDidAddMessageNotification, object: BotClient.shared)
}
#objc
func handleKeyboardNotification(notification: Notification) {
switch notification.name {
case UIResponder.keyboardDidChangeFrameNotification:
if let rect = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
DispatchQueue.main.async {
//print("r \(rect) i \(self.tableView.safeAreaInsets)")
// print(self.tableView.contentInset)
let inset = self.tableView.contentInset
self.updateContentInset(inset: UIEdgeInsets(top: (rect.height + 10) - self.tableView.safeAreaInsets.top, left: inset.left, bottom: inset.bottom, right: inset.right))
}
}
default: return
}
}
func updateContentInset(inset: UIEdgeInsets) {
UIView.animate(withDuration: 0.15, animations: {
self.tableView.contentInset = inset
}, completion: { f in
if self.messages.count > 0 {
self.tableView.scrollToRow(at: IndexPath.init(row: 0, section: 0), at: .top, animated: true)
}
})
}
#objc func handleMessageAdded(notification: Notification) {
DispatchQueue.main.async {
if self.isVisible {
self.tableView.reloadData()
} else {
self.needsUpdateViewOnAppearance = true
}
}
}
func getContentInset() -> UIEdgeInsets {
return UIEdgeInsets(top: max(tableView.safeAreaInsets.top, messageBar.frame.height) - tableView.adjustedContentInset.top, left: 0, bottom: tableView.safeAreaInsets.bottom - tableView.adjustedContentInset.bottom, right: 0)
}
// Err
func displayError(_ message: String?) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}