I have a custom headerview with a "+" (add) button and a "<" back button, along with a header title.
In most cases this works when implemented. I use delegates on the buttons and implement them in my viewcontrollers. I simply just pop the viewcontroller with the back-button.
class StrandparkenHeader: UIView {
weak var delegateBack: tapBack?
weak var delegateAdd: tapAdd?
private let imageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "strandparken_illu")
return imageView
}()
private let headerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.clear
return view
}()
private let header: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = UIFont(name: "Arial", size: 35)
label.textAlignment = .center
label.adjustsFontSizeToFitWidth = true
return label
}()
private let backButton: UIButton = {
let btn = UIButton(type: UIButton.ButtonType.custom) as UIButton
btn.setImage(UIImage(named: "backButton"), for: .normal)
btn.addTarget(self, action: #selector(tapToBack), for: .touchUpInside)
return btn
}()
private let addButton: UIButton = {
let btn = UIButton(type: UIButton.ButtonType.custom) as UIButton
btn.setImage(UIImage(named: "ic_add_24px"), for: .normal)
btn.addTarget(self, action: #selector(tapToAdd), for: .touchUpInside)
return btn
}()
override init(frame: CGRect){
super.init(frame: frame)
setupView()
setupContraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setHeaderTitle(title: String) {
header.text = title
}
func setHeaderOnly(onlyHeader: Bool) {
if onlyHeader == true {
imageView.isHidden = true
self.snp.updateConstraints { update in
update.height.equalTo(60)
update.width.equalTo(UIScreen.main.bounds.width)
}
super.updateConstraints()
}
}
func toggleBackButton(isVisible: Bool) {
if isVisible == false {
backButton.isHidden = true
} else {
backButton.isHidden = false
}
}
func toggleAddButton(isVisible: Bool) {
if isVisible == false {
addButton.isHidden = true
} else {
addButton.isHidden = false
}
}
private func setupView(){
self.backgroundColor = .strandGreen
self.layer.borderWidth = 0.3
self.layer.borderColor = UIColor.darkGray.cgColor
addSubview(imageView)
addSubview(headerView)
headerView.addSubview(header)
headerView.addSubview(backButton)
headerView.addSubview(addButton)
}
private func setupContraints() {
self.snp.makeConstraints { make in
make.height.equalTo(330)
make.width.equalTo(UIScreen.main.bounds.width)
}
imageView.snp.makeConstraints { make in
make.top.equalTo(headerView.snp.bottom)
//make.bottom.equalToSuperview().offset(-5)
make.width.equalToSuperview()
}
headerView.snp.makeConstraints { make in
make.width.equalToSuperview()
make.top.equalTo(safeAreaLayoutGuide.snp.topMargin)
make.height.equalTo(60)
//make.bottom.equalTo(imageView.snp.top)
}
header.snp.makeConstraints { make in
make.center.equalToSuperview()
make.height.equalTo(40)
make.width.equalTo(250)
}
backButton.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(20)
}
addButton.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-20)
}
}
@objc func tapToBack() {
print("IN HV")
delegateBack?.tapToBack()
}
@objc func tapToAdd() {
delegateAdd?.tapToAdd()
}
}
However, in two cases now, I have an issue where the back button just doesn't work.
In both cases I set the delegate in viewDidLoad (like everywhere else) and I implement the popViewController like I would everywhere else. It just doesn't seem to work in those two cases.
In both cases it's viewcontrollers that have been send from a tableview cell to a viewcontroller. Both cases the viewcontrollers are pushed form within a viewcontroller that implements a tableview.
In one case I have a tableview with posts, and when clicking a post, User is send to a detail view over that post. If I don't click on the view below the implemented headerview, I can use the back butten just fine. If I click on the view below the headerview, and then click on the back button, it stops working.
import UIKit
class PostDetailViewController: UIViewController, savePost, tapBack, updatePost, deletePost {
let headerView = StrandparkenHeader()
let contentView = PostDetailView()
var header = String()
var postIndex = Int()
var isNew = true
override func viewWillAppear(_ animated: Bool) {
if isNew == true {
postConfigNew()
} else {
postConfigExisting()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
headerView.setHeaderTitle(title: header)
headerView.toggleAddButton(isVisible: false)
headerView.delegateBack = self
view.addSubview(contentView)
view.addSubview(headerView)
setupConstraints()
}
private func postConfigNew() {
contentView.setupAsNew(date: getDate())
print("IN POST DETAIL - UID: \(UserHandler.getCurrentAuthID())")
let userID = UserHandler.getCurrentAuthID()
UserHandler.readUser(userID: userID) { User in
self.contentView.setupProfileName(name: User.name)
print(User.name)
UserHandler.downloadProfileImage(imageName: User.imageName) { (image) in
self.contentView.setupProfilePicture(image: image)
}
}
contentView.delegateSave = self
}
private func postConfigExisting() {
let post = PostHandler.postList[postIndex]
print("POST ID IS: \(post.postID)")
headerView.setHeaderTitle(title: "Opslag")
let uid = UserHandler.getCurrentAuthID()
if uid == post.owner {
contentView.setupExistingPost(isOwner: true, post: post)
} else {
contentView.setupExistingPost(isOwner: false, post: post)
}
UserHandler.readUser(userID: post.owner) { user in
self.contentView.setupProfileName(name: user.name)
UserHandler.downloadProfileImage(imageName: user.imageName) { img in
self.contentView.setupProfilePicture(image: img)
}
}
contentView.delegateUpdate = self
contentView.delegateDelete = self
}
private func getTime() -> String {
let date = Date()
let calender = Calendar.current
let hour = calender.component(.hour, from: date)
let minute = calender.component(.minute, from: date)
return "\(hour):\(minute)"
}
private func getDate() -> String {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM-yyyy"
let result = formatter.string(from: date)
return result
}
private func setupConstraints() {
headerView.snp.makeConstraints { make in
make.top.equalToSuperview()
}
contentView.snp.makeConstraints { make in
make.top.equalTo(headerView.snp.bottom)
make.width.equalToSuperview()
make.bottom.equalToSuperview()
}
}
func savePostTapped() {
let getheader = contentView.getHeader(isNew: true)
let body = contentView.getBody()
if getheader != "" && body != "" {
PostHandler.createPost(userID: UserHandler.getCurrentAuthID(), header: getheader, body: body, date: getDate(), time: getTime())
let alert = UIAlertController(title: "Success", message: "Dit opslag blev lagt op.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
self.navigationController?.popViewController(animated: true)
}))
self.present(alert, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "Fejl", message: "Overskrift og/eller opslagstekst mangler.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
func updatePostTapped() {
let getheader = contentView.getHeader(isNew: false)
let getbody = contentView.getBody()
let post = PostHandler.postList[postIndex]
print("POST ID IS: \(post.postID)")
if getheader != "" && getbody != "" {
PostHandler.updatePost(post: post)
let alert = UIAlertController(title: "Success", message: "Dit opslag blev redigeret.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
self.navigationController?.popViewController(animated: true)
}))
self.present(alert, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "Fejl", message: "Overskrift og/eller opslagstekst mangler.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
func deletePostTapped() {
let post = PostHandler.postList[postIndex]
PostHandler.deletePost(post: post)
let alert = UIAlertController(title: "Success", message: "Dit opslag blev slettet.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
self.navigationController?.popViewController(animated: true)
}))
self.present(alert, animated: true, completion: nil)
}
func tapToBack() {
//print("POP")
navigationController?.popViewController(animated: true)
}
}
In the other case I have a tableview with cells of names on PDF's in firebase. When clicking on a cell, User is send to a viewcontroller with a pdfview, and I can't use the back button.
I recently changed from use webView to pdfview, and when I used webview, it worked....
import UIKit
import PDFKit
class ReadPDFViewController: UIViewController, tapBack {
var headerTitle = String()
var path = String()
let pdfView = PDFView(frame: .zero)
let header = StrandparkenHeader()
let pdfContentView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
header.delegateBack = self
setupView()
setupConstraints()
}
private func setupView() {
header.toggleBackButton(isVisible: true)
header.toggleAddButton(isVisible: false)
header.setHeaderTitle(title: headerTitle)
header.setHeaderOnly(onlyHeader: true)
view.addSubview(pdfContentView)
view.addSubview(header)
pdfContentView.addSubview(pdfView)
pdfView.autoScales = true
pdfView.displayMode = .singlePageContinuous
pdfView.displaysPageBreaks = true
PDFHandler.getPDFURL(path: path) { (URL) in
self.pdfView.document = PDFDocument(url: URL)
}
}
private func setupConstraints() {
header.snp.makeConstraints { make in
make.top.equalToSuperview()
make.bottom.equalTo(view.safeAreaLayoutGuide.snp.topMargin).offset(60)
}
pdfContentView.snp.makeConstraints { make in
make.top.equalTo(header.snp.bottom)
make.left.right.bottom.equalToSuperview()
}
pdfView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
func tapToBack() {
navigationController?.popViewController(animated: true)
}
}
I'm not using storyboard, and I use snapkit for constraints. I have tried to add the headerview as subview to view as the first view, also tried to add it last to see if it made a difference.
I tried to look at the view hierarchy, and nothing seem to block the view.
When I click the back button, it shows it's being clicked, but neither the headerViews method (where it delegates) or the actual implemented delegated method in the viewcontroller is being called.