UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Button stops working when interacting with another view

Forums > Swift

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.

2      

Hi,

it is really hard to make sense of so much code.. if you add breakpoints to:

@objc func tapToBack() {
      print("IN HV")
      delegateBack?.tapToBack()
  }

  @objc func tapToAdd() {
      delegateAdd?.tapToAdd()
  }

are they hit? Next step would be to verify the delegate is indeed there and wasn't deallocated.

Also wouldnt using the native UINavigationController be easier?

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.