How can I create a handler, for dynamically created buttons, in the parent view controller? - swift

I created a subview from the ViewController. In the subview, multiple buttons are created. When any of the created buttons are tapped, I want to call the function buttonTapped() in the parent view controller.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var bestTeamEver = SFGiants(frame: CGRect(x: 0, y: 100, width: view.bounds.width, height: 40))
view.addSubview(bestTeamEver)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buttonTapped(){
NSLog("BUTTON INSIDE SFGIANTS TAPPPED")
}
}
class SFGiants: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
for i in 0...10 {
var new_x = i*44
var button = UIButton(frame: CGRect(x: new_x, y: 0, width: 40, height: 40))
button.backgroundColor = UIColor.orangeColor()
// Call buttonTapped() in parent view controller
button.addTarget(self, action: "buttonTapped:", forControlEvents: UIControlEvents.TouchDown)
self.addSubview(button)
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

You can create a customize init method and can pass reference to parent viewController and set that to button target.
init(frame: CGRect actionListener:UIViewController) {
super.init(frame: frame)
for i in 0...10 {
var new_x = i*44
var button = UIButton(frame: CGRect(x: new_x, y: 0, width: 40, height: 40))
button.backgroundColor = UIColor.orangeColor()
// Call buttonTapped() in parent view controller
button.addTarget(actionListener, action: "buttonTapped:", forControlEvents: UIControlEvents.TouchDown)
self.addSubview(button)
}
}

Related

Animating a class that is an extension of UIView in Swift

I'm trying to get an instance of my Options class to animate onto the screen using the function appear() which activates when pressing a button in my SearchResult class but I'm having trouble, the instance has been added as a subview to the view in the ViewController Class.
I've been able to use the function optionMenu.appear() in ViewController and that works as it should but not when it's called through clicking the button in SearchResult, it seems to do everything but the animation as I've tested printing in optionMenu.appear() and it successfully prints.
import Foundation
import UIKit
class SearchResult {
private let link: String
var body = UIView()
init(_ link: String){
self.link = link
let options = UIButton(x: Int(body.bounds.width-60), y: 10, size: 50.0)
options.addTarget(self, action: #selector(optionsClicked), for: .touchUpInside)
body.addSubview(options)
}
#objc func optionsClicked(){
ViewController().optionMenu.appear()
}
}
import Foundation
import UIKit
class Options: UIView{
var buttons = [UIButton]()
private var link = String()
init(_ titles: [String]){
let height = ((titles.count+1)*65)+5
super.init(frame: CGRect(x: 0, y: Int(UIScreen.main.bounds.height)-height, width: Int(UIScreen.main.bounds.width), height: height))
var i = 0
for title in titles{
let button = UIButton(frame: CGRect(x: 5, y: 5+(i*65), width: Int(UIScreen.main.bounds.width)-10, height: 60))
button.setTitle(title, for: .normal)
buttons.append(button)
self.addSubview(button)
i += 1
}
let cancel = UIButton(frame: CGRect(x: 5, y: 5+(i*65), width: Int(UIScreen.main.bounds.width)-10, height: 60))
cancel.setTitle("cancel", for: .normal)
self.addSubview(cancel)
}
func appear(){
UIView.animate(withDuration: 0.2, animations: {
self.transform = CGAffineTransform(translationX: 0, y: -self.bounds.height)
})
}
#objc func disappear(){
UIView.animate(withDuration: 0.2, animations: {
self.transform = CGAffineTransform.identity
})
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
It's because you are constructing a new Viewcontroller, and calling appear on that Viewcontroller's option menu every time optionsClicked() is called. You need a reference to the viewController that has the option menu and do myViewController.optionMenu.appear()

Creating a button from a subclass not appearing in view

Will someone please explain why the following code is not placing a button in my view?
Button Class
class CustomButton: UIButton {
var button = UIButton()
func makeButton() {
button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
button.center = self.center
button.setTitle("PRESS", for: .normal)
button.backgroundColor = UIColor.red
button.addTarget(self, action: #selector(ViewController.buttonPressed), for: .touchUpInside)
self.addSubview(button)
}
}
Main Class
class ViewController: UIViewController {
var button: CustomButton?
override func viewDidLoad() {
super.viewDidLoad()
button?.makeButton()
}
#objc func buttonPressed() {
print("woohoo!")
}
}
You need to completely redo the CustomButton class. It should not have a button property. It should initialize itself.
And the setting of the target belongs in the view controller, not the button class.
Updated button code:
class CustomButton: UIButton {
init() {
super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
setTitle("PRESS", for: .normal)
backgroundColor = UIColor.red
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Updated view controller:
class ViewController: UIViewController {
var button = CustomButton()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
}
#objc func buttonPressed() {
print("woohoo!")
}
}

How to bring a subview to front when selected in Swift?

So I've got two views, both added to the superview programmatically:
//First added view to my superView (baseView)
let imageViewOne = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
baseView.addSubview(imageViewOne)
//Second added view to my superView (baseView)
let imageViewTwo = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
baseView.addSubview(imageViewTwo)
I want to bring one of them to the front when it is selected because when you add a new view, it overlaps the previous one. Something that works with "selected" or "touched".
Use this:
baseView.bringSubview(toFront: imageViewOne)
You can do this by UITapGestureRecognizer by setting Restoration ID from the interface builder
func tapOneAct(sender: UITapGestureRecognizer) {
if(sender.view!.restorationIdentifier == "view1")
// view1 is `Restoration ID` of imageViewOne
{
view.bringSubview(toFront: imageViewOne)
}
if(sender.view!.restorationIdentifier == "view2")
// view2 is `Restoration ID` of 2nd view
{
view.bringSubview(toFront: imageViewTwo)
}
}
If I understand correctly then you would like to check the status of selected view and bring it to the front of the view hierarchy.
In this case you have to do following things:
1) track selection state, let it will be var isSelected: Bool in code snippet
2) hold a callback for an action when view is selected (you can use for example UIButton to observe tap event)
In following code snippet you can check the selection state when the view is touched.
BaseView implements logic for checking the state of SelectionView.
import UIKit
final class SelectionView<T: UIView>: UIView {
var onTap: ((_ isSelected: Bool) -> ())?
// MARK: - Private
private let view: T
private let button = UIButton(type: .custom)
private var isSelected: Bool = false
// MARK: - Init
init(view: T) {
self.view = view
super.init(frame: .zero)
setUp()
}
// MARK: - Private
private func setUp() {
addSubview(view)
addSubview(button)
button.addTarget(self, action: #selector(onButtonTap), for: .touchUpInside)
}
#objc private func onButtonTap() {
isSelected = !isSelected
onTap?(isSelected)
}
// MARK: - Layout
override func layoutSubviews() {
super.layoutSubviews()
view.frame = bounds
button.frame = bounds
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return view.sizeThatFits(size)
}
// MARK: - Unused
override init(frame: CGRect) {
fatalError("init(frame:) has not been implemented")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
final class BaseView: UIView {
init() {
super.init(frame: .zero)
let views = [SelectionView(view: UIView()), SelectionView(view: UIView())]
for view in views {
view.onTap = { [weak self, weak view] isSelected in
guard let selectedView = view
else { return }
if isSelected {
self?.bringSubview(toFront: selectedView)
}
}
}
}
// MARK: - Unused
override init(frame: CGRect) {
fatalError("init(frame:) has not been implemented")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

How to create a custom view in the header of a form

I would like to know if its possible to create something like the image below.
I guess I have to create a custom view but any clue on how to do it?
Thanks
If you have a UITableView on the screen, you can achieve this by creating a custom table header view:
Create a subclass of UIView -> CustomTableHeaderView
Add a button inside it:
class CustomTableHeaderView: UIView {
var rightButton: UIButton
override init(frame: CGRect) {
rightButton = UIButton()
let buttonWidth: CGFloat = 100
// this is hardcoded for cimplicity, it's better to setup auto layout constraints here
rightButton.frame = CGRect(x: 0, y: frame.width - buttonWidth, width: buttonWidth, height: 50)
rightButton.setTitle("My button", forState: .Normal)
// other configuration
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In your view controller (or any other place where you initialise a table view, set your custom view as a tableHeaderView:
tableView.tableHeaderView = CustomTableHeaderView(frame: frame:CGRect(x: 0, y: 0, width: view.frame.width, height: 10))

Must I use protocols / delegation to have a ViewController perform an action of a UIButton created in another class?

I created a UIButton as a subview of a UIView Class. I would like to add that UIView as a subview to a UIViewController Class. I would like to add a target action in the UIViewController Class to the UIButton. In my example below, when you tap on my UIButton, it should print "Hello the button was pressed!". However, nothing happens. Why is this? I understand there are other ways of achieving this. Why doesn't my way work and what are the pros and cons of achieving this using other techniques?
import UIKit
class ViewController: UIViewController {
let myView = MyView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(myView)
myView.myButton.addTarget(self, action: #selector(hello), forControlEvents: UIControlEvents.TouchUpInside)
}
func hello()
{
print("Hello the button was pressed!")
}
}
class MyView : UIView
{
var myMainView = UIView(frame: CGRectZero)
var myButton = UIButton(frame: CGRectZero)
override init(frame:CGRect){
super.init(frame:frame)
setUpViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setUpViews()
{
print("Set up views")
myMainView.frame = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width , UIScreen.mainScreen().bounds.height)
myMainView.backgroundColor = UIColor.yellowColor()
self.addSubview(myMainView)
myButton.frame = CGRectMake(100, 100, 200, 40)
myButton.backgroundColor = UIColor.darkGrayColor()
myMainView.addSubview(myButton)
}
}
You need to give myView a size.
Like:
override func viewDidLoad() {
super.viewDidLoad()
myView.frame = view.bounds
self.view.addSubview(myView)
myView.myButton.addTarget(self, action: #selector(hello), forControlEvents: UIControlEvents.TouchUpInside)
}