NavigationBarItem misplaced at first app start - swift

I have a strange issue with my NavigationBarItems. After first app start (when application was not running in background) the BarButton is misplaced (see Screenshot 1). The Button title should be "PDF".
Screenshot 1
However, when i press the home button and open up the app again (from background), the position is correct (Screenshot 2).
Screenshot 2
I can´t figure out what the problem is. I use a custom titleView for the navigation bar, which looks like that:
class TitleView : UIView {
var titleLabel:UILabel!
init(title:String) {
super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 44))
titleLabel = UILabel()
titleLabel.textAlignment = .Center
titleLabel.font = UIFont.normalFont(15)
titleLabel.text = title.uppercaseString
titleLabel.textColor = UIColor.primaryColor()
self.addSubview(titleLabel)
titleLabel.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.snp_edges)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension UIViewController {
func setTitleView(title:String) {
self.navigationItem.titleView = TitleView(title: title)
self.view.backgroundColor = UIColor.whiteColor()
}
And i initialize the navigationTitle and item in viewDidLoad as follows:
self.setTitleView("Tanzkarte")
let sendDanceCardButton = UIBarButtonItem(title: "PDF", style: .Plain, target: self, action: #selector(DanceCardController.sendDanceCard))
self.navigationItem.rightBarButtonItem = sendDanceCardButton
I didn`t find any solution for that problem in the internet and hope someone of you has a solution for it.
EDIT: the custom title view is not the issue. Even when I don´t use any title for the navigation bar, the button is misplaced.

Your title view is initialized with an explicit width which probably exceeds the maximum width allowed by the navigation bar. Try to init it with zero size, and call sizeToFit() after initialization.

Related

White Space at the bottom of a tableview

I have a viewcontroller with a tableview inside it. At the bottom of the tableview there is some white space that I want to get rid of and be replaced by my background colour.
I have added and customised the tableview programatically so I am looking for a programatic answer, as I did not use storyboard much for this. (I had only used it to setup my TabBarController and link it to navigation and view-controllers.)
Below is the some of the code I used to configure the tableview.
private let tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.register(ProfileTableViewCell.self, forCellReuseIdentifier: ProfileTableViewCell.identifier)
tableView.backgroundColor = Constants.backgroundColor
return tableView
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.frame = view.bounds
}
Im sure the answer is probably just a single line of code, but I couldn't find one that worked. Thanks in advance!
***EDIT
I have found a solution, but I still think there is a better way to solve this problem. The code below sets a UIView as the background of the tableview, then changes the colour of the UIView.
private let tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.register(ProfileTableViewCell.self, forCellReuseIdentifier: ProfileTableViewCell.identifier)
let backgroundView = UIView(frame: CGRect(x: 0,
y: 0,
width: tableView.bounds.size.width,
height: tableView.bounds.size.height))
backgroundView.backgroundColor = Constants.backgroundColor
tableView.backgroundView = backgroundView
return tableView
}()
The default color of the containing view controller is showing, since the table is short. Try setting the view controller's view's background color in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad();
view.backgroundColor = Constants.backgroundColor
}

NSTouchBar not releasing last touched view

I am making an app which uses the NSTouchBar.
The touchbar is made by the NSWindowController.makeTouchBar()method.
In this touchbar I can place NSCustomTouchBarItems.
I have made two NSCustomTouchBarItems.
The first one sets a view to a default ui button, with this:
let item001 = NSCustomTouchBarItem(identifier: someIdentifier)
item001.view = NSButton(title: "myButton", target: nil, action: nil)
The second one sets a viewController, with this:
let item002 = NSCustomTouchBarItem(identifier: someIdentifier)
item002.viewController = TestViewController()
The TestViewController only loads a simple view inside its loadView()
method.
class TestViewController: NSViewController {
override func loadView() {
self.view = TestView001(frame: NSRect(x: 0, y: 0, width: 100, height: 30))
}
}
The TestView001 only creates a background color so you can see it.
TestView001 has the following code:
class TestView001: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
print("TestView001.init()")
// Create a background color.
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.green.cgColor
}
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
All of this works perfectly.
But when I have touched the second item inside the touchbar,
and then close my app's window.
The windowController and everything else is nicely released
from memory.
But I can still see that TestView001 is in memory and not being
released.
When using a standard ui button like in item001, then you don't
have this problem.
It looks like some NSTouch still has a reference to the view
if you look at this image:
However, I do not completely understand this image.
What is the best way of solving this.
Thanks in advance.

NSTextField created from custom class appears randomly

I have a main NSView with 2 NSView subviews containing each a custom NSButton (all created in Interface Builder). My custom button class is programmatically creating an NSTextField below the buttons, by adding it from the superview:
And the custom NSButton class code:
class buttonUpDown: NSButton {
var myLabel:NSTextField!
required public init?(coder: NSCoder) {
super.init(coder: coder)
let labelWidth=CGFloat(90)
print("init")
superview?.autoresizesSubviews = false
myLabel = NSTextField(frame: NSMakeRect(frame.origin.x+frame.size.width/2-labelWidth/2,frame.origin.y-20,labelWidth,16))
myLabel?.stringValue = "Mesh Quality"
myLabel.alignment = .center
myLabel.font? = .systemFont(ofSize: 8)
myLabel.backgroundColor = .red
myLabel.isBordered = false
myLabel.isEditable = false
superview!.addSubview(myLabel)
}
}
When I have a single Button referencing the class, the label appears properly.
But when I have 2, only 1 label is appearing randomly left or right:
What do I miss to have a text displayed below each button ?
Found the issue. Creating a label in the init function will give erratic positioning of the view frame. Adding them from the draw func, makes it more reliable.

UITapGestureRecognizer works first time, but not second time

[Greatly simplified]
I'm following a "Let's Build That App" YouTube series on Firebase 3. It's from 2016, so I've had to rework some of the code since Swift has evolved since then, but mostly it's a useful tutorial.
But, I'm stuck on something.
The red box is intended to be a custom titleView with an Image and Name, but I've simplified it to try to find the problem.
viewWillAppear calls setupNavbar which sets up the navbar.titleView:
func setupNavbar() {
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 300, height: 40)
titleView.backgroundColor = .red
let containerView = UIView() // for the Image and Label, later
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = .green
// left, top, width, height anchors equal to same for titleView
containerView.leftAnchor.constraint(equalTo: titleView.leftAnchor)
// top, width, height are similar
titleView.addSubview(containerView)
self.navigationItem.titleView = titleView
let recognizer = UITapGestureRecognizer(target: self, action: #selector(showChatController))
titleView.addGestureRecognizer(recognizer)
}
The selector's function is:
#objc func showChatController() {
let chatController = ChatLogTableViewController()
navigationController?.pushViewController(chatController, animated: true)
}
The class ChatLogTableViewController has just the default viewDidLoad().
First, I'm surprised the box is red, and not green. Why is that?
Second, if I click the red box, the ChatController is loaded, as expected. But, when I click "Back" and return to this screen, the red box is not visible, thus I can no longer tap on it. BUT....If I sign out and sign in/up again, the box is red and I can click it again.
What am I missing?
Update 1: The "+" creates a new controller and presents it.
present(UINavigationController(
rootViewController: NewMessageTableViewController()),
animated: true,
completion: nil)
That controller is currently empty, except for a leftBarButtonItem which is just a barButtonSystemItem (.cancel). Just like "Sign Out", this also "resets" the gesture and it works.
Update 2: Code added upon request.
Update 3: question greatly simplified. Also, if I change the showChatController code to just print ("show Chat Controller"), I can click to my heart's content; the red box and its tap gesture both remain.
So, there is something happening when I ...pushViewController and then come back.
Have you tried to set isUserInteractionEnabled property of the label which you set as the navBar's titleView? I know it's silly but I've missed this a lot of times :)
let recognizer = UITapGestureRecognizer(target: self, action: #selector(YourViewController.titleWasTapped))
titleView.isUserInteractionEnabled = true
titleView.addGestureRecognizer(recognizer)
you can try following things:
1. "User Interaction Enabled" in the UILabel Attributes Inspector in the Storyboard.
2. override func viewDidLoad(animated: Bool) {
super.viewDidAppear(animated)
self.titleView.userInteractionEnabled = true
var tapGesture = titleView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showChatController)))
self.titleView.addGestureRecognizer(tapGesture)
}
func showChatController() {
println("Tap Gesture recognized")
}
Hi #Zonker I've tested your setupNavBar(). It's working fine nothing worng with your code when I press and go back clicking titleView. Reason why you're not getting .green color is you have to give height and width for containerView and big reason is you've put addSubView code below the anchor and there is no .isActive = true in your containerView.leftAnchor.constraint... Please check setupNavBar() code below:
func setupNavbar() {
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 300, height: 40)
titleView.backgroundColor = .red
let containerView = UIView() // for the Image and Label, later
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = .green
titleView.addSubview(containerView)
// left, top, width, height anchors equal to same for titleView
containerView.widthAnchor.constraint(equalToConstant: 300).isActive = true
containerView.heightAnchor.constraint(equalToConstant: 40).isActive = true
containerView.leftAnchor.constraint(equalTo: titleView.leftAnchor).isActive = true
// top, width, height are similar
self.navigationItem.titleView = titleView
let recognizer = UITapGestureRecognizer(target: self, action: #selector(showChatController))
titleView.addGestureRecognizer(recognizer)
}
It would better if you push your code in github so we can test your code

Apple iOS Tutorial: Problems getting UIButton addTarget to Work

I am working through the "Start Developing iOS Apps" tutorial provided by Apple, and having a problem in the Implement a Custom Control section. I have tried everything to get the button to print something to the console, but can't get it to work. I am on the section in the tutorial "Add Buttons to the View" about a fifth of the way down the page.
Everything else works fine.
I have set up the RatingControl.swift file as follows:
import UIKit
class RatingControl: UIView {
// MARK: Initialisation
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Create a square button
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
// Set the background colour of the button
button.backgroundColor = UIColor.darkGray
button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), for: .touchDown)
// Add the button to the view
addSubview(button)
}
// MARK: Button Action
func ratingButtonTapped(_: UIButton) {
print("Button pressed")
}
}
A few things there are slightly different to the tutorial, but that is because Xcode says things have changed.
In my Main.storyboard file I have added a View using the RatingControl class. The button displays but nothing happens when I click it.
Any help would be appreciated. If any extra information is needed please let me know.
There is actually a hierarchy of three views in this story:
A stack view (not mentioned in the original question)
The RatingControl
The button
Well, the stack view, a complicated control whose job involves a lot of voodoo with the constraints of its arranged subviews, is reducing the size of the RatingControl to zero. The button is thus outside the RatingControl's bounds (easily proved by giving the RatingControl a background color — the background color doesn't appear). The RatingControl does not clip to its bounds, so the button is still visible — but, being outside its superview, it is not tappable.
As for why the stack view is reducing the size of the RatingControl to zero: it's because the RatingControl has no intrisicContentSize. The reason why we need this is complicated and has to do with how a stack view manipulates its arranged subviews and gives them constraints. But in essence, it makes a lot of its decisions based on an arranged view's intrinsicContentSize.
In the comments you said:
In size inspector it has intrinsic size set to placeholder, width 240, height 44. Or is there something I need to do to stop the stack view making it zero sized?
The key thing to understand here is that that setting in the size inspector is only a placeholder. It is merely to shut the storyboard up so that it won't complain. It doesn't actually give the view any intrinsic size at runtime. To do that, you must actually override the intrinsicContentSize property, in your code, as a computed variable returning an actual size.
Looks like you're stumbling through the "Start Developing iOS Apps (Swift)" tutorial before it's updated for iOS 10 and Swift 3. Amazing you've made it this far! Anyhow, #matt has a great technical explanation below but did not include the actual code to get your tutorial working:
Add the following to your class definition (outside the init function):
override var intrinsicContentSize: CGSize {
return CGSize(width: 240, height: 44)
}
then your code will work : -
class RatingView: UIView {
// MARK: Initialisation
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Create a square button
let button = UIButton(frame: CGRect(x: 200, y: 100, width: 44, height: 44))
// Set the background colour of the button
button.backgroundColor = UIColor.darkGray
button.addTarget(self, action: #selector(self.ratingButtonTapped(_:)), for: .touchDown)
// Add the button to the view
addSubview(button)
}
// MARK: Button Action
func ratingButtonTapped(_: UIButton) {
print("Button pressed")
}
}