I made a scrollable screen with a static tableview.
But I want to create a UIButton that is independent of scrolling this table view.
I want to anchor it to the bottom of the view as shown below.
Is it possible to dock a button like that at the bottom of the screen in a scrollable static tableview?
You should create a footer view for tableView to do this.
Programmatically
let footerView = UIView()
footerView.backgroundColor = .red // your color
// You have to give height and width yourself
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.setTitle("<title>", for: .normal)
button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
footerView.addSubview(button)
via Interface Builder
Simply you should drag and drop UIView (CMD + Shift + L > type uiview) to your tableView and then add a button to your footerView, set constraints.
Then you should add this footerView to your tableView
tableView.tableFooterView = footerView
Create an action for your button
#objc func didTapButton(_ sender: UIButton) {
print("didTapButton called")
}
Related
I have a view with a number of subviews. The subviews all have size constraints, built in the storyboard.
part of viewDidLoad() is to programmatically add a number of buttons. And I'm using the subview's bounds to known where to add the buttons to take up available screen real estate. As a simplified example:
override func viewDidLoad()
{
super.viewDidLoad()
let bottom = buttonView.bounds.maxY
let right = buttonView.bounds.maxX
let button = UIButton()
button.frame = CGRect(x: CGFloat(0.0),
y: CGFloat(0.0),
width: CGFloat(right),
height: CGFloat(bottom))
button.addTarget(self, action: #selector(buttonPushed(_:)), for: .touchUpInside)
buttonView.addSubview(button)
print("buttonview frame = \(buttonView.frame)")
}
override func viewWillAppear()
{
super.viewWillAppear()
print("buttonview frame = \(buttonView.frame)")
}
What I see happen in the simulator (and on hardware) is that the views are all sized according to how I had it designed in the storyboard, matching the hardware I had designed it on (iPhone 11). When I run it on an iPhone 8, my subviews all run off screen, and the first print statement indicates a width of 414.0. The constraints are applied sometime before viewWillAppear, and the second print statement indicates a width of 375.0. But my button isn't resized to fit. It still continues off screen.
How do I programmatically add my button subviews (this example only has the 1, code has 20 being built in a loop) to adhere to the constraints of the superview bounds, after I know what they actually are?
you have to add constraint by code, for example.
1) first I will create the button.
lazy var closeButton : UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .blue
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}()
2) with this code I will add the button to the main view, and set the constraint.
self.view.addSubview(closeButton)
NSLayoutConstraint.activate([
closeButton.heightAnchor.constraint(equalToConstant: 20),
closeButton.widthAnchor.constraint(equalToConstant: 20),
closeButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20),
closeButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)]
but if you want to understand really how work this please the follow link
How to add constraints programmatically using Swift
How to create UIButton in UITableViewController like as AirBnB map.
Highlighted in red in screenshot.
It's quite easy actually, just create the table view as you always do (either storyboard or code) then create the button, position it correctly, set constraints and make sure it's not a subview of the table view but the view that contains the table view.
Since you didn't specify whether you use the storyboard or not, I did use it for the example below.
See this extremely simple demo I've just created:
This is the view hierarchy:
And these are the constraints for the button:
Create a button first and then then override the function scrollViewDidScroll():
import UIKit
class YourTableViewController: UITableViewController {
private let button = UIButton(type: UIButton.ButtonType.custom) as UIButton
override func viewDidLoad() {
let image = UIImage(named: "Image.png")
button.frame = CGRect(x: yourXpos, y: yourYPos, width: 60, height: 60)
button.setImage(image, for: .normal)
button.clipsToBounds = true
button.layer.cornerRadius = 30
button.addTarget(self, action: #selector(buttonClicked(_:)), for:.touchUpInside)
areaOfTableView.addSubview(button)
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = self.areaOfTableView.contentOffset.y
button.frame = CGRect(x: yourXpos, y: offset + yourYpos, width: btn.frame.size.width, height: btn.frame.size.height)
}
#objc private func buttonClicked(_ button: UIButton) {
// Action when you tapped the button
}
}
I would like to place a "forgot?" Button into my Password Textfield. If nothing is in the Textfield the user should be able to click it and another ViewController should pop up. The only thing I managed to do is what you can see in the picture down below. My problem is that the button is not clickable and that it is not on the same level as the placeholder text. Any ideas on how to solve this two problems?
let button = UIButton(type: .custom)
button.setTitle("vergessen?", for: .normal)
button.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(100), height: CGFloat(100))
button.addTarget(self, action: #selector(self.refresh), for: .touchUpInside)
passwordTextField.rightView = button
passwordTextField.rightViewMode = .unlessEditing
In the file you have subclassed from my answer add another function in that file
// Modify the values as required
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
let offset = -20
let width = 100
let height = width
let x = Int(bounds.width) - width - offset
let y = offset
let rightViewBounds = CGRect(x: x, y: y, width: width, height: height)
return rightViewBounds
}
Now you can remove the follow line
button.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(100), height: CGFloat(100))
Output
And regarding the button click event. Remove your code as you mention its not connected
IBAction func refresh(_ sender: Any) { }
And add the following code in the same file where the button is created.
#objc func refresh() {
// your vc code here
print("in refresh")
}
The above code hooks in with addTarget code you have.
button.addTarget(self, action: #selector(self.refresh), for: .touchUpInside)
Hope this helps.
I suggest to create a xib file and its relevant view for text items that have a button inside it. So you would be able to reuse it elsewhere in the future (this or another projects)
By defining constant height (100) you will experience ugly and misplaced UI in different iOS devices.
Here it is what you should do :
Define a xib file for your custom UITextView
Create constraints for it so it width and height defined by its parent.Also define your forgot UIButton in relative to your UITextView.
Define its (xib) relevant UIView class
Use it in your Storyboard
You can use Storyboard. Make a helper view for the password TextField and Forgot Button.
-Set the Helper view same width and height with the email TextField.
-Add a TextField and a Button inside the helper view and then you can decide for the password TextField width and the Forgot Button width.
-Set constrains for the TextField and Button
I set green color to understand what the helper View does.
Update
I just used your code and it works fine.Check your textfield constrains again. This is what I used.
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(type: .system)
button.setTitle("vergessen?", for: .normal)
button.setTitleColor(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1), for: .normal)
button.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(100), height: CGFloat(100))
button.addTarget(self, action: #selector(self.refresh), for: .touchUpInside)
textfFeld.rightView = button
textfFeld.rightViewMode = .unlessEditing
}
#objc func refresh(_ sender: Any) {
print("Hello")
}
The only think I changed is the button type from .custom to .system.
On App Start, I highlight one of my BarButtonItems. Since iOS11 I am not able to do this anymore because I need to initialise it on another way.
First my old ios 10 code. I saved my barButtonItemView after initialising it.
let item1 = UIBarButtonItem(image: UIImage(named: "iconProfile"), style: .plain, target: self, action: #selector(pushSettings))
self.navigationItem.setRightBarButtonItems([item1], animated: true)
if let view = self.navigationItem.rightBarButtonItem?.value(forKey: "view") as? UIView {
barButtonItemView = view
}
Now I was able to access the position of barButtonItem just by using the frame
barButtonItemView.frame.topMiddle.x
With ios 11 the picture on my UIBarButtonItem gets smaller. I was reading that I have to resize them by using constraints. So now I initialise my UIBarButtons by using this function:
let imageView = UIImageView(image: UIImage(named: imageName))
imageView.frame = CGRect(x: 5, y: 7, width: 20, height: 20)
let button = UIButton()
button.frame = CGRect(x: 0, y: 0, width: 30, height: 34)
button.addTarget(self, action: action, for: .touchUpInside)
button.widthAnchor.constraint(equalToConstant: 30).isActive = true
button.heightAnchor.constraint(equalToConstant: 34).isActive = true
button.addSubview(imageView)
let barItem = UIBarButtonItem(customView: button)
return barItem
I set my rightBarButtonItem on the same way and try to get the view on the same way.
self.navigationItem.setRightBarButtonItems([item1], animated: true)
if let view = self.navigationItem.rightBarButtonItem?.value(forKey: "view") as? UIView {
barButtonItemView = view
}
But this time my barButtonItemView has the x and y values set to 0,0. Has anyone an idea how I can get the position of my BarButtonItem depending on my screen?
What I tried
The first solution I tried is to call layoutIfNeeded() before I try to get the frame again. This is not working as well as the x and y values are still 0,0.
First, if your UIBarButtonItem only needs to display an UIImage, you should use [UIBarButtonItem initWithImage:style:target:action:] instead of customView for more standard layout. And if you want to custom the size of UIBarButtonItem, you can resize the image.
Second, before iOS 10, UIBarButtonItem's view was added in the UINavigationBar directly, but after iOS 11, UIBarButtonItem was added in a UIStackView, and the stackView was added in the UINavigationBar, so if you get a (0, 0) from the customView's frame.origin, it means that the customView is layout at the (0, 0) origin in the stackView.
Third, the view of the UIBarButtonItem would not be initialized until the UINavigationBar was shown and your UIBarButtonItem was put in the bar, so you can not get the view when the UIBarButtonItem just be initialized. Maybe you can try to get the view and its frame in your viewController's viewWillAppear: or viewDidAppear:.
I would like to make the navigationbar title to be clickable. When user click on it, it should perform a segue. But I have no idea how to do this.
I have tried the following to get the title and apply the Tap gesture to it.
var subviews = self.navigationController?.navigationBar.subviews
if let subviews = subviews {
// Better check for array length before accessing to the 1st element
var subview = subviews [0]
}
but its giving me error Variable subview inferred to have type AvyObject, which may be unexpected
One more approach to add button as a title of navigation controller.
You need to set navigation item title view to your button object
Create button object in viewDidLoad() method:
Swift 4.0 Edit
let button = UIButton(type: .custom)
button.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
button.backgroundColor = .red
button.setTitle("Button", for: .normal)
button.addTarget(self, action: #selector(clickOnButton), for: .touchUpInside)
navigationItem.titleView = button
Here you can see in last line button is directly set to the titleView of navigationItem which will add button at the center of navigation bar.
Action method for button is below:
#objc func clickOnButton() {
}
Kampai's answer updated for Swift 3:
let button = UIButton(type: .custom)
button.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
button.setTitle("Button", for: .normal)
button.addTarget(self, action: #selector(self.clickOnButton), for: .touchUpInside)
self.navigationItem.titleView = button
To get rid of that error, specify a type for your if let constant. I'd also recommend changing that if let constant name since it's the same as the one already declared in the previous line.
var subviews = self.navigationController?.navigationBar.subviews
if let subviewArray:NSArray = subviews {
// Better check for array length before accessing to the 1st element
var subview:UILabel = subviewArray[0] // <-- If the subview's a UILabel
}