So I am using a custom View (.xib) which contains couple of objects: UIImage view & UIButton. The custom view in contained and used in View controller UIView , however I can't seem to figure out how to detect if the button(from .xib) is tapped and launch a function in the view controller. Any suggestions?
I can However add UITap gesture to the entire Viewcontroller UIView, however Thats not what i need.
#IBAction func calculateTapped(sender : AnyObject) {
//You can get action here
}
Step 1
Link Your all button to to xib cocoa class
class DialogHandler: UIView {
#IBOutlet weak var acceptBtn: UIButton!
#IBOutlet weak var closeBtn: UIButton!
}
step 2
Add Below code in viewDidLoad()
if let customView = Bundle.main.loadNibNamed("DialogDemo", owner: self, options: nil)?.first as? DialogHandler {
dialogView = customView
customView.acceptBtn.addTarget(self, action: #selector(ViewController.acceptBtnPressed(sender:)), for: .touchUpInside)
}
step 3
Create Function for Button that can handle your click event
#objc func acceptBtnPressed (sender:UIButton) { print("Button Pressed") }
Related
I have created a UIView class (SegmentControl.swift) and added the UISegmentControl using an xib. I have another viewcontroller where I refer this UIView class to add the segment control.
My question here is since I have the UISegmentControl in another UIView class, how will I update the index and load the respective containerViews in the ViewController?
#IBAction func didChangeIndex(_ sender: UISegmentedControl) { } will work only if I have Segment control in ViewController.
Please provide your suggestions on how can I load the containerviews when the UIControlSegment is in another class.
This is the code I have:
SegmentControl.swift
class SegmentControl: UIView {
#IBOutlet weak var segmentView: UISegmentedControl!
// Loading nibs are there.. just that I didn't include here
func create(titles: [String]) {
items.enumerated().forEach { (index, item) in
segmentView.setTitle(item, forSegmentAt: index)
}
}
}
ViewController.swift
func showSegmentControl() {
let segmentedView = GenericSegmentedView.create(items: ["A", "B"])
stackView.addArrangedSubView(segmentedView)
}
Two container views --> aInfoView and bInfoView are intialised in View Controller.
How will I load them on switching the segments since they are in UIView class. I couldnt find any answers here. Please help!
Thank you!!
You can add a target to the segmented control even if it's contained within another view. The segmented control is an accessible property on your SegmentControl class. From the SegmentControl's parent view controller add the target action to the your SegmentControl's child segmentView.
func showSegmentControl() {
let segmentedView = GenericSegmentedView.create(items: ["A", "B"])
stackView.addArrangedSubView(segmentedView)
segmentedView.segmentView.addTarget(self, action: #selector(segmentControlChanged(_:)), for: .valueChanged)
}
// Function will be called when value changed on the SegmentControl's segmentView
#objc func segmentControlChanged(_ sender: UISegmentedControl) {
print(sender.selectedSegmentIndex)
}
Something as aside, interchanging segmentControl and segmentView can be confusing to people not familiar with your code. Maybe a more descriptive name for the SegmentControl like SegmentControlContainer or something would help make the distinction.
We want Lyft button touch event because I am working in analytics, so, I need how many people choose Lyft but I can't put UIView click event. I try below code.
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.checkAction))
cell.lyftButton.addGestureRecognizer(gesture)
How can i achieve this?
You can directly assign a selector method to lyftButton e.g
lyftButton.addTarget(self, action: #selector(lyftButtonAction(_:)), for: .touchUpInside)
#objc
func lyftButtonAction(_sender: UIButton) {
//Do your action
}
To retrieve the LyftButton, you'll need to fetch the button inside the Lyft view, after retrieving it, I tried to add another target to it which was your 'checkAction' method, but for some reason it is not being called. One workaround solution is:
On Auto Layout, created a transparent button on top of the Lyft Button View, let's callet it 'Transparent Lyft Button': Example (I've embeded in another view because it was on a stackView);
On the code, retrieved the button with the above method, held it in a variable, let's call it 'requestLyftButton' and disabled it.
Created an IBAction for the 'Transparent Lyft Button' that triggers the method 'self.checkAction' that you've created and also calls requestLyftButton.sendActions(for: .touchUpInside), which triggers the original Lyft SDK action.
To Retrieve Lyft UIButton:
#IBOutlet weak var lyftButton: LyftButton!
#IBOutlet weak var transparentLyftButton: UIButton!
var requestLyftButton: UIButton?
func retrieveLyftButton(in view: UIView) {
for view in view.subviews {
if let lyftBtn = view as? UIButton {
lyftBtn.isEnabled = false
requestLyftButton = lyftBtn
} else {
retrieveLyftBtn(in: view)
}
}
}
transparentLyftButton IBAction to trigger your method + lyft sdk original action:
#IBAction func requestLyft(_ sender: UIButton) {
if let lyftBtn = requestLyftButton {
checkAction() // Your method
lyftBtn.sendActions(for: .touchUpInside)
}
}
I hope that you can understand what was done, if you have any questions, just let me know.
So I have a 'ListViewController' that has a subview called 'ListView'
ListView is the master view that holds all remaining subviews.
Inside listView, there is a subview called PhotoView and inside it, it has a UIButton that toggles the constraints of PhotoView, resizing based on arbitrary values.
Hierarchy is as follows:
ListViewController <- ListView <- PhotoView
The reason I've done this is to minimize the amount of code inside each view, to segment it out.
My question is: Who should be in charge of the 'resizing' function? My
understanding is that a viewController should generally handle the
main functions that are inside it pertaining to its subviews.
However in this case, it is being handled inside its subviews subview, 'PhotoView'. Should I use protocols to persist it to the ListViewController? PhotoView(Protocol) -> ListView(Protocol) -> ListViewController?
Is this a correct method? Or is there a more efficient way of handling subview functions?
Thanks guys
If you move all logics into ListViewController, everything is very simple as you can assign any UIControl in the VC:
class ListViewController : UIViewController{
#IBOutlet var goButton: UIButton! //In the DetailView
#IBOutlet var photoView: PhotoView!
#IBOutlet var detailView: DetailView!
// MARK: --
#IBAction func testCommand(_ sender: Any){
PhotoView.backgroundColor = UIColor.red
detailView.backgroundColor = UIColor.red
}
// MARK: --
#IBAction func photoViewCommand(_ sender: Any){
PhotoView.backgroundColor = UIColor.red
detailView.backgroundColor = UIColor.red
}
// MARK: --
#IBAction func detailViewCommand(_ sender: Any){
PhotoView.backgroundColor = UIColor.red
detailView.backgroundColor = UIColor.red
}
}
You don't need to even know the ListView. Use IB can assign all UIViews , not only direct children, but all subview trees, including UIControls, like buttons and labels , as long as they lies in the scene of same UIViewController and their methods is in the same VC not in other separated views.
If you have the code of views, you can move it to the VC easily. Just one class to control all. If you need to know who is who. Adding Mark is enough for small files.
I'm new in programming and I'm stuck with this little problem. I created a table view with several items, that passes data to a label and an image view through a segue. It all works fine, but now I want to make the image clickable, in order to segue to another view controller to show this image expanded. How can I do that?
class ViewController: UIViewController {
#IBOutlet var titleLabel: UILabel!
#IBOutlet var descLabel: UILabel!
#IBOutlet var imageView: UIImageView!
var titleName: String?
var descName: String?
var imageName: String?
func configureView() {
if let poster = self.imageName {
if let imagePoster = self.imageView {
imagePoster.image = UIImage (named: poster)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.text = titleName
descLabel.text = descName
self.configureView()
}
The easiest thing to do is to use a UIButton instead of an image view. Set it's type to custom and install the image into the button in IB (Interface Builder).
That way you can trigger an IBAction just like any other button. It also highlights on a touch like you'd expect, triggers on touch up rather than touch down, etc.
If you don't want to use a button-with-image, you have to set userInteractionEnabled = YES on the image view and install a tap gesture recognizer on it. See the docs on UITapGestureRecognizer for more information.
I have created a UIViewController. Within this view controller I have a container view.
In the code I instantiate the container view like so:
var answerButtons: AnswerButtonsViewController!
override func viewDidLoad() {
super.viewDidLoad()
answerButtons = AnswerButtonsViewController()
answerButtons.delegate = self
println(answerButtons.name) // This prints out the correct string
println(answerButtons.answerOne) // This prints out nil
println(answerButtons.buttons) // This prints out an empty array
retrieveSelectedCategory(selectedCategory!)
}
However the container view is definitely instantiate because within the container view class I set the background color of the UIButton's and this displays correctly within my app.
Here is the code from my container view:
#IBOutlet var answerOne: UIButton!
#IBOutlet var answerTwo: UIButton!
#IBOutlet var answerThree: UIButton!
#IBOutlet var answerFour: UIButton!
#IBOutlet var answerFive: UIButton!
#IBOutlet var answerSix: UIButton!
#IBOutlet var answerSeven: UIButton!
#IBOutlet var answerEight: UIButton!
var buttons: Array<UIButton> = []
var name = "Is this set"
var delegate: AnswerButtonsViewControllerDelegate?
override func viewWillAppear(animated: Bool) {
addAnswerButtonsToArray()
buttons.map(customiseButtons)
}
func addAnswerButtonsToArray() {
buttons.append(answerOne)
buttons.append(answerTwo)
buttons.append(answerThree)
buttons.append(answerFour)
buttons.append(answerFive)
buttons.append(answerSix)
buttons.append(answerSeven)
buttons.append(answerEight)
}
func customiseButtons(button: UIButton) {
button.layer.cornerRadius = 5
button.setTitleColor(UIColor.blackColor(), forState: .Normal)
button.backgroundColor = GSNColor.veryLightGrayColor()
var title: String! = button.titleLabel?.text
title.replaceRange(title.startIndex...title.startIndex, with: String(title[title.startIndex]).capitalizedString)
button.titleLabel?.text = title
}
#IBAction func answerButtonPressed(sender: UIButton) {
delegate?.answerButtonPressed(sender)
}
Also the IBAction of the buttons work.
So I can't understand why my property answerButtons is initiated and the name property is set ok. However the properties are nil when printed and the array empty. Yet the view is being displayed correctly.
Any insights would be much appreciated and if you need any more details please let me know.
Edit
Here is a picture of storyboard with the connections:
The controller embedded in the container view is instantiated at the same time that the controller with the container view is. You should not instantiate it yourself. You only need to get a reference to it. A controller in a container view is a child view controller of the controller whose subview is the container view. Instead of this,
answerButtons = AnswerButtonsViewController() // this creates another instance that you never use, and is never on screen
You should have this,
answerButtons = self.childViewControllers[0] as AnswerButtonsViewController
Here, you are just instantiating the view controller.You only get a reference to it.You have to instantiate the view controller using storyboard
let SB: UIStoryboard = UIStoryboard(name: StoryboardName, bundle: Bundle.main)
let VC = SB.instantiateViewController(withIdentifier: StoryboardIdOfViewController)
if let _ = VC.view {
//Do here
}
The IBOutlet's will be nil until the view is loaded. All you are doing is instantiating the container view controller, not displaying or loading the view.
The IBOutlet's won't be available until the AnswerViewController's loadView and viewDidLoad method's are called.
You can check:
if (answerButtons.isViewLoaded()) {
}
To see if the view is loaded. If you want the view to be loaded you'll either need to access the view property of the controller, or add the view to the view hierarchy as a subview.