I have been trying to fix an issue that I encountered with a tableviewcontroller.
The sections within the tableviewcontroller are views:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionLabel = UILabel()
sectionLabel.text = Catalog.sharedInstance.allSections[section - 1].name.localized(lang: defaults.string(forKey: "language")!)
sectionLabel.font = UIFont(name: "Centabel Book", size: 25)
sectionLabel.backgroundColor = UIColor.white
sectionLabel.clipsToBounds = true
return sectionLabel
}
If I try to add a button programatically to put it over the tableview
let actionButton = JJFloatingActionButton()
override func viewDidLoad() {
super.viewDidLoad()
// Configuration of the Floating Action Button
actionButton.buttonColor = .red
actionButton.addItem { item in
self.performSegue(withIdentifier: "goToSettings", sender: nil)
}
actionButton.display(inViewController: self)
actionButton.clipsToBounds = true
// This doesn't work. It is to bring the button to the front. Now it is hidden by the sections.
view.bringSubviewToFront(actionButton)
// Checks if the language setting is nil, which means it is the first time you run the application. If then... selects English as default.
if defaults.string(forKey: "language") == nil {
defaults.set("en", forKey: "language")
}
}
... I don't know why but the viewForHeaderInSection hides the button. You can check it in the picture below:
floating button hided by the headersection
I tried to use the method:
view.bringSubviewToFront(actionButton)
and also:
actionbutton.superview?.bringSubviewToFront(actionButton)
But none of this brings the button to the front.
I am using a floating action button from github called JJFloatingActionButton. But I tried to add a simple UIButton and I got the same error. This is the code that also gave me the same error inside viewDidLoad:
let button = UIButton(frame: CGRect(x: 100, y: 1000, width: 100, height: 50))
button.backgroundColor = .green
button.setTitle("Test Button", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
Again the same error. You can check the picture:
adding a simple UIButton happens the same problem
May be you can help me with that.
Thanks in advance.
You could try to use a regular UIViewController (not UITableViewController) and add the UITableView by hand.
Like that you would have better control over the view hierarchy.
Related
I add a custom button to the tabBar in my MyViewController.viewDidLoad(subclass of UITabBarController)
But I find it doesn't response the selector.
If I delay one second to add button(in DispatchQueue.main.asyncAfter closure) ,it works OK.
I think it's not the right way to resolve it.
func addButton() {
let button = UIButton(type: UIButton.ButtonType.custom)
button.bounds = CGRect(x:0,y:0,width:30,height:30);
button.backgroundColor = UIColor.red
button.center = CGPoint(x:self.tabBar.frame.size.width/2, y:self.tabBar.frame.size.height/2 - 20);
button.addTarget(self, action: #selector(click(button:)), for: UIControl.Event.touchUpInside)
tabBar.addSubview(button)
}
You have added button to UITabBar of UITabBarController as half of part of the button would appear above the Tabbar and half of below the Tabbar as per frame.
So I guess you will not get click on part of that button which is out of Tabbar(above Tabbar) would not get touch. I you will make button little big OR try to click with arrow in simulator, you will get idea.
If you need to have button at bottom but slightly upper, then please create custom Tabbar to achieve design like this. Or else you can add that button into UITabBarController’s view instead of Tabbar.
class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.addButton()
}
func addButton() {
let button = UIButton(type: UIButton.ButtonType.custom)
button.bounds = CGRect(x:0,y:0,width:50,height:50); //1
button.backgroundColor = UIColor.purple
button.center = CGPoint(x:self.tabBar.frame.size.width/2, y:self.tabBar.frame.size.height/2 - 50 + self.tabBar.frame.origin.y); //2
button.addTarget(self, action: #selector(click(button:)), for: UIControl.Event.touchUpInside)
button.layer.cornerRadius = button.frame.size.height/2
button.layer.masksToBounds = false
button.layer.shadowColor = UIColor.black.withAlphaComponent(0.5).cgColor
button.layer.shadowRadius = 5.0
button.layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
button.layer.shadowOpacity = 0.5
//tabBar.addSubview(button) //3
self.view.addSubview(button). //4
}
#objc func click(button: UIButton) {
print("Button get clicked")
}
}
I have marked four things with commented by numbers at the end of lines, that you can make to your code and try.
My view (in the Main.storyboard) contains another UIView which takes only about 80% of the screen's height (set with constraints).
In this second view (I call it the viewContainer) I want to display different views, which works with
viewContainer.bringSubview(toFront: someView)
I created a new group in my project which contains 3 UIViewController classes and 3 xib files which I want to display in my viewContainer.
For each of those xib files I changed the background color to something unique so I can tell if it's working. And it does so far.
Now I tried adding a UIButton to the first UIViewController class and added an #IBAction for it. That just prints text to the console.
When I run the app I can switch between my 3 classes (3 different background colors) and I can also see and click the button I added to the first class when I select it.
But the code is never executed and my console is empty. Is that because my 3 other Views are not shown in my Main.storyboard?
SimpleVC1.swift
import UIKit
class SimpleVC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func onButtonPressed(_ sender: Any) {
print("test")
}
}
Try using a target instead (and programmatically create the button), it might be easier:
import UIKit
class SimpleVC1: UIViewController {
var button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
button.frame.size = CGSize(width: 100, height: 100)
button.center = CGPoint(x: 100, y: 100)
button.setTitle("Control 1", for: .normal)
button.addTarget(self, action: #selector(control1), for: .touchUpInside)
button.backgroundColor = UIColor.blue
button.setTitleColor(UIColor.white, for: .normal)
}
#objc func control1() {
//Add your code for when the button is pressed here
button.backgroundColor = UIColor.red
}
}
I have a scrollview inside a view. Inside the scrollview I create programmatically 5 buttons. Every button loads a different image with a different tag each one. I added a function that is called when pressing the buttons.
let avatarsListScrollingView = avatarsListView(CGSizeMake(70.0, 55.0), avatarCount: 5)
func avatarsListView(buttonSize:CGSize, avatarCount:Int) -> UIView {
**CODE**
for i in 0...(avatarCount-1) {
let button = UIButton(type: .Custom)
**CODE**
button.setImage(UIImage(named: avatarsList[i]), forState: .Normal)
button.tag = i
button.addTarget(self, action: "avatarListSelected:", forControlEvents: .TouchUpInside)
avatarButtonView.addSubview(button)
}
return avatarButtonView
}
Then when pressing the buttons, I call to "avatarListSelected":
func avatarListSelected(sender:UIButton){
if let image = sender.imageView?.image?.imageWithRenderingMode(.AlwaysTemplate) {
sender.setImage(image, forState: .Normal)
sender.tintColor = UIColor.redColor()
}
self.addAvatarView.reloadInputViews()
}
This function tints the image button to red, it is working fine, but the problem is that when I press some other button, I want the other one goes to the original color. Right now every button that I press gets in red.
I tried to add the call to "self.addAvatarView.reloadInputViews()" to try to "redraw" again all the buttons, but never gets called.
Do you guys know some way to do this?
Thanks to everybody!
This is the final code that solved the problem:
func avatarListSelected(sender:UIButton){
print(sender.tag)
if let image = sender.imageView?.image?.imageWithRenderingMode(.AlwaysTemplate) {
sender.setImage(image, forState: .Normal)
sender.tintColor = UIColor.redColor()
}
for view in self.avatarButtonView.subviews as [UIView] {
if let btn = view as? UIButton {
if btn.tag != sender.tag {
btn.setImage(UIImage(named: avatarsList[btn.tag]), forState: .Normal)
}
}
}
}
Create a property selectedButton: UIButton? and keep a reference to the selected button there. Don't forget to update it in avatarListSelected method and before you change it, if it isn't nil, change its color to original (and then change it).
If the buttons have different original colors, subclass UIButton class and keep the original color there.
I don't know if is better approach or answer, but, i maybe could delivery this using this approach:
Create a method that will "fill" the color for your choice button and "clear" color to others , but its a method that loop through UIScrollView and look for each UIButton. Something like this :
func setBackgroundColorButton(color:UIColor , buttonTag:Int){
for view in self.scrollView.subviews as [UIView] {
if let btn = view as? UIButton {
if btn == buttonTag {
btn.tintColor = color
} else {
btn.tintColor = UIColor.whiteColor()
}
}
}
}
This is the concept, i didn't tested, but maybe just need adjust to search inside your scroll view or similar.
But with this will be work nice i believe :D
You could do it like this
for i in 0...5 {
let button = UIButton(type: .Custom)
let x = 50 * i + 10
let y = 50
button.frame = CGRectMake(CGFloat(x), CGFloat(y), 40, 40)
button.setTitle("\(i)", forState: .Normal)
button.tag = i
button.backgroundColor = UIColor.greenColor()
button.tintColor = UIColor.blueColor()
button.addTarget(self, action: "avatarListSelected:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func avatarListSelected(sender : UIButton){
sender.backgroundColor = UIColor.orangeColor()
for view in self.view.subviews{
if(view.isKindOfClass(UIButton)){
let button = view as! UIButton
if button.tag != sender.tag{
button.backgroundColor = UIColor.greenColor()
}
}
}
}
The frame etc is just for demonstation purpose only, you should of course use your own value. The tintColor property is not valid for all button types. Read the documentation for more information.
This seems to have been asked a few times in swift and objc, but I can't see a correct answer for swift so hopefully someone can help me this time. I have created a custom accessory view button, but need the correct button action: as "accessoryButtonTapped" is an unrecognised selector.
What is the selector needed to call the tableViewCellDelegate method willSelectRowAtIndexPath?
The code I have is:
let cellAudioButton = UIButton(type: .Custom)
cellAudioButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
cellAudioButton.addTarget(self, action: "accessoryButtonTapped", forControlEvents: .TouchUpInside) //INCORRECT ACTION:
cellAudioButton.setImage(UIImage(named: "blueSpeaker.png"), forState: .Normal)
cellAudioButton.contentMode = .ScaleAspectFit
cell.accessoryView = cellAudioButton as UIView
Thanks in advance to anyone who can help.
Why do you need to call willSelectRowAtIndexPath? You have done everything right and to solve your unrecognized selector error just make a function that will be called when you tap on the cell.accessoryView. In your case:
func accessoryButtonTapped(){
print("Tapped")
}
Update
If you want to get the indexPath you could just
Add a tag to your cellAudioButton:
cellAudioButton.tag = indexPath.row
In your addTarget add a : to pass a parameter
And in your function
func accessoryButtonTapped(sender : AnyObject){
print(sender.tag)
print("Tapped")
}
So the whole code:
let cellAudioButton = UIButton(type: .Custom)
cellAudioButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
cellAudioButton.addTarget(self, action: "accessoryButtonTapped:", forControlEvents: .TouchUpInside)
cellAudioButton.setImage(UIImage(named: "blueSpeaker.png"), forState: .Normal)
cellAudioButton.contentMode = .ScaleAspectFit
cellAudioButton.tag = indexPath.row
cell.accessoryView = cellAudioButton as UIView
func accessoryButtonTapped(sender : AnyObject){
print(sender.tag)
print("Tapped")
}
Swift 3.1 Updated Solution of Rashwan
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
//add button as accessory view
let cellAudioButton = UIButton(type: .custom)
cellAudioButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
cellAudioButton.addTarget(self, action: #selector(ViewController.accessoryButtonTapped(sender:)), for: .touchUpInside)
cellAudioButton.setImage(UIImage(named: "closeRed"), for: .normal)
cellAudioButton.contentMode = .scaleAspectFit
cellAudioButton.tag = indexPath.row
cell.accessoryView = cellAudioButton as UIView
return cell
}
func accessoryButtonTapped(sender : UIButton){
print(sender.tag)
print("Tapped")
}
Note:
Here ViewController is the name of Class like LoginViewController
Why don't you use the UITableViewDelegate?
#available(iOS 2.0, *)
optional func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
To clarify that, yet it is possible. You can have custom UI elements act as accessory button.
Here is an example – assuming you're using Interface Builder. Add a UISwitch to the custom cell. Now right-click drag-n-drop from the UITableViewCell to the UISwitch. Select accessoryView as the Outlet connection. Done.
Every time you press the switch button the delegate method will be fired. This should also work for other action elements, e.g., UIButtons. And that is exactly the use case OP asked for.
EDIT
This is my current code with the programmed buttons.
override func viewDidLoad() {
super.viewDidLoad()
self.playVideo()
var filter = UIView()
filter.frame = self.view.frame
filter.backgroundColor = UIColor.orangeColor()
filter.alpha = 0.15
self.view.addSubview(filter)
// Do any additional setup after loading the view, typically from a nib.
//Add Images
var logoImage : UIImageView
logoImage = UIImageView(frame:CGRectMake(136, 50, 102, 104));
logoImage.image = UIImage(named:"patchicon.png")
self.view.addSubview(logoImage)
var textLogo : UIImageView
textLogo = UIImageView(frame:CGRectMake(51, 0, 273, 371));
textLogo.image = UIImage(named:"logopatch.png")
self.view.addSubview(textLogo)
//Add Buttons
let buttonLogin = UIButton.buttonWithType(.Custom) as! UIButton
buttonLogin.frame = CGRectMake(16, 519, 343, 60)
buttonLogin.layer.cornerRadius = 0.2 * buttonLogin.bounds.size.width
buttonLogin.addTarget(self, action: "buttonTouched", forControlEvents: UIControlEvents.TouchUpInside)
buttonLogin.setImage(UIImage(named:"loginButton.png"), forState: .Normal)
view.addSubview(buttonLogin)
let buttonRegister = UIButton.buttonWithType(.Custom) as! UIButton
buttonRegister.frame = CGRectMake(16, 587, 343, 60)
buttonRegister.layer.cornerRadius = 0.2 * buttonRegister.bounds.size.width
buttonRegister.addTarget(self, action: "buttonTouched2", forControlEvents: UIControlEvents.TouchUpInside)
buttonRegister.setImage(UIImage(named:"registerButton.png"), forState: .Normal)
view.addSubview(buttonRegister)
}
func buttonTouched()
{
performSegueWithIdentifier("loginTouched", sender: nil)
}
func buttonTouched2()
{
performSegueWithIdentifier("registerTouched", sender: nil)
}
This is what my Storyboard looks like:
http://i.stack.imgur.com/MlEhI.png
How can I program these buttons further to switch into new view controllers? For instance, I want to click the register button and I want the current MainViewController to switch to a new one that will have the information for the user to sign up and so forth...
Thanks!
First, you need to add a target to the button:
buttonLogin.addTarget(self, action: "buttonTouched", forControlEvents: UIControlEvents.TouchUpInside)
And then, add this function:
func buttonTouched()
{
println("touched")
}
To switch to a new view controller, take a look at this
Well, to switch to a "new" view controller, you need to first create that view controller in your storyboard. After that, do you see that line that links the main view controller to the other two? Click that line and go to "Attributes Inspector". In the identifier blank space, fill with whatever you want and then, in the button that you created programmatically, you need to set this:
performSegueWithIdentifier("whateverYouWant", sender: self)
Oh, and don't forget this step: select your main view controller, go to Editor > Embed In > Navigation Controller. If you don't do this, your application will crash.
Hope that helps!
EDIT: Try this:
Write some function like this outside the viewDidLoad method:
func segueToNextVC(sender: UIButton!) {
println("Button pressed, let's see what happens!")
performSegueWithIdentifier("whateverYouWant", sender: self)
}
Then, in your button in viewDidLoad, do like this:
buttonLogin.addTarget(self, action: "segueToNextVC:", forControlEvents: UIControlEvents.TouchUpInside)
Note: Don't forget the : in segueToNextVC:, that's important!
add target to your button..
buttonLogin.addTarget(self, action: "btnpressed:", forControlEvents: .TouchUpInside)
Vtwot is my Second viewController and its Storyboard Id is ViewTwoId..
make UIButtons action like these..
func btnpressed(sender: UIButton!)
{
let jj = self.storyboard!.instantiateViewControllerWithIdentifier("ViewTwoId") as! Vtwot
self.navigationController!.pushViewController(jj, animated: true)
}
i hope it helps..