how to dismiss a UIView when tapped outside its bounds? - ios

I have a View Controller that has a main view, inside that i have created a small drop down(a pop up menu) towards the right that is also a UIView. I have added a button that shows and dismisses this pop up menu view when tapped.
All is working fine.
But now I want it to dismiss the view when the user taps outside those bounds.
The current code is as follows
var vw = UIView()
func popup(){
vw = UIView(frame: CGRectMake(self.view.frame.size.width - 210, 20, 260, 320))
vw.backgroundColor = UIColor.whiteColor()
vw.layer.cornerRadius = 5.0
vw.layer.shadowColor = UIColor.blackColor().CGColor
vw.layer.shadowOpacity = 0.8
vw.layer.shadowRadius = 3.0
vw.layer.shadowOffset = CGSizeMake(2.0, 2.0)
let name: UILabel = UILabel(frame: CGRectMake(5, 0, 90, 30))
name.text = testUserName
name.font = UIFont(name: "Abc Lt", size: 20.0)
let orgName: UILabel = UILabel(frame: CGRectMake(5, 35, 90, 30))
orgName.text = "Abc Lt"
name.font = UIFont(name: "V Lt", size: 25.0)
let profileImageView: UIImageView = UIImageView(frame: CGRectMake(137, 4, 35, 35))
profileImageView.image = UIImage(named: "profilepic.png")
let dropUpButton: UIButton = UIButton(frame: CGRectMake(profileImageView.frame.origin.x + 42, 18, 15, 10))
// dropUpButton.imageView.image = [UIImage imageNamed:#"dropdown.png"];
dropUpButton.setImage(UIImage(named: "dropup.png"), forState: .Normal)
dropUpButton.addTarget(self, action: #selector(LandingPageViewController.hideView), forControlEvents: .TouchUpInside)
vw.addSubview(name)
vw.addSubview(orgName)
vw.addSubview(profileImageView)
vw.addSubview(dropUpButton)
self.drawLine()
let tableView: UITableView = UITableView(frame: CGRectMake(0, 70, vw.frame.size.width, vw.frame.size.height-40), style: .Plain)
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.scrollEnabled = false
vw.addSubview(tableView)
self.navigationController!.view!.addSubview(vw)
}
//the function that displays and dismiss the pop up menu
func hideView() {
vw.hidden = true
}
I have read some posts where one can use a tap gesture to find where it's tapped, and based on that using an if/else condition it can be solved but I am not sure.
Also you can check the image that I have uploaded for better understanding:

What you can really do is to take main superview full size same as your screen size. give clear background to it.Take a contentview same as you are doing now. Add a button similar size to super view and on click selector you can remove view from superview.
There are also alternative ways for doing this by identifying touch location. But It takes lots of effort.
Hope this helps you.

You can use tap gesture to do that
add this code on main view:
let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(_:)))
tap.delegate = self
self.view.addGestureRecognizer(tap)
and then
func handleTap(sender: UITapGestureRecognizer? = nil)
{
hideView()
}

Related

Make custom title view click able swift

I customise the title view add a image and labels in it know I want to make it clickable but its not working. I added tap gesture in title view but its not working.
here is my code.
func navTitleWithImageAndText(titleText: String,subTitleText:String, imageName: String) -> UIView {
// Creates a new UIView
let titleView = UIView()
let label = UILabel(frame: CGRect(x: 0, y: -15, width: 0, height: 0))
// Creates a new text label
// let label = UILabel()
label.text = titleText
label.font = UIFont.boldSystemFont(ofSize: 17)
//label.center = titleView.center
label.textColor = .white
//label.textAlignment = NSTextAlignment.center
label.sizeToFit()
let subtitleLabel = UILabel(frame: CGRect(x: 0, y: 5, width: 0, height: 0))
subtitleLabel.backgroundColor = UIColor.clear
subtitleLabel.textColor = UIColor.white
subtitleLabel.font = UIFont.systemFont(ofSize: 12)
subtitleLabel.text = subTitleText
subtitleLabel.sizeToFit()
// Creates the image view
let image = UIImageView()
image.image = UIImage(named: imageName)
// Maintains the image's aspect ratio:
let imageAspect = image.image!.size.width / image.image!.size.height
// Sets the image frame so that it's immediately before the text:
let imageX = label.frame.origin.x - label.frame.size.height * imageAspect - 20
let imageY = label.frame.origin.y
let imageWidth = label.frame.size.height * imageAspect + 15
let imageHeight = label.frame.size.height + 15
image.frame = CGRect(x: imageX, y: imageY, width: imageWidth, height: imageHeight)
image.contentMode = UIView.ContentMode.scaleAspectFit
// Adds both the label and image view to the titleView
titleView.addSubview(label)
titleView.addSubview(image)
titleView.addSubview(subtitleLabel)
// Sets the titleView frame to fit within the UINavigation Title
titleView.sizeToFit()
return titleView
}
add tap gesture in it. but why its not working.
titleView.addGestureRecognizer(titleGestureRecognizer)
I have implemented a tap gesture on a label in the following way:-
Initialize Variable
var tap_gest = UITapGestureRecognizer()
Add gesture on Label. Add the following code in "viewDidLoad()"
if normal label then use below
self.register_as_company_lbl.isUserInteractionEnabled = true // register_as_company_lbl is my label where tap gesture added
self.tap_gest.addTarget(self, action: #selector(registerAsComapany))
self.register_as_company_lbl.addGestureRecognizer(tap_gest)
if on navigation bar title then use below
self.navigationController?.navigationBar.addGestureRecognizer(tap_gest)
Define Selector method in your ViewController Class as following
#objc func registerAsComapany(){//Add your task what you perform, In my case I have to push to another screen
let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CompanyDetailsViewController") as! CompanyDetailsViewController
self.navigationController?.pushViewController(vc , animated: true)
}
titleView.isUserInteractionEnabled = true

Is this a good way to show a view by touch a button at the middle of the navigation bar and remove it by touch anywhere else?

I'm trying to put a button at the middle of the navigation bar, it will show a list when I touch it (I added a pure UIView here instead of a UITabeView to just make the code simpler) . And then the additional view will be removed when I touch anywhere else. So I add a background view whose size is the same as the screen to response my touch. Although it still behind the navigation bar.
Here is my question:
Is this a good implementation?
class ViewController: UIViewController {
var optionView: UIView!
var backgroundView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(ViewController.titleButtonTapped), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 20, height: 30)
button.backgroundColor = .red
navigationItem.titleView = button
}
func titleButtonTapped() {
backgroundView = UIView(frame: UIScreen.main.bounds)
backgroundView.backgroundColor = .clear
let gesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleGesture)) // add this gesture to response my touch
backgroundView.addGestureRecognizer(gesture)
view.addSubview(maskView)
optionView = UIView(frame: CGRect(x: -40, y: 30, width: 100, height: 100)) // x = button.wdith / 2 - optionView.width / 2
optionView.backgroundColor = .red
navigationItem.titleView?.addSubview(alertView)
}
func handleGesture() {
optionView.removeFromSuperview()
backgroundView.removeFromSuperview()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Now it looks like the following.
Edit:
The following is my implementation of a popover view.
func buttonTapped() {
let popoverViewController = UIViewController()
popoverViewController.preferredContentSize = CGSize(width: 300, height: 300)
popoverViewController.modalPresentationStyle = .popover
let presentationController = popoverViewController.popoverPresentationController!
presentationController.delegate = self
presentationController.sourceView = view
presentationController.sourceRect = CGRect(x: 100, y: 100 , width: 100, height: 100)
present(popoverViewController, animated: true, completion: nil)
}
// delegate
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
It's a little bit different frrom the Apple documentation. They recommended that we'd better configure the presentation controller after calling present(:animated: completion:) method. But it doesn't work if I don't configure it before presentation. Maybe, because I set the delegate.
Configuring the popover presentation controller after calling present(_:animated:completion:) might seem counter-intuitive but UIKit does not create a presentation controller until after you initiate a presentation. In addition, UIKit must wait until the next update cycle to display new content onscreen anyway. That delay gives you time to configure the presentation controller for your popover.
For using a popover or not, it depends on the purpose of this pop over view. If it has lots of information, it will be better to separate it out to another view controller and make segue to it on button click. This will provides user the full screen to look at whatever it is.
For me, adding a button at the center of a navigation bar is not usual. You have to inform me about it for me to click on it.
In conclusion:
If you want a popover view to tell user hints or show them something, it will be better to use UIPopoverPresentationController so that you don't need to care about the styles.
If you want another view to show data, list of pictures etc, it will be better to use a segmented control or another view controller
var optionView: UIView!
var backgroundView: UIView!
override func viewDidLoad()
{
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(ViewController.titleButtonTapped), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 20, height: 30)
button.backgroundColor = .red
navigationItem.titleView = button
}
func titleButtonTapped()
{
if backgroundView == nil
{
backgroundView = UIView(frame: UIScreen.main.bounds)
backgroundView.backgroundColor = .clear
let gesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleGesture)) // add this gesture to response my touch
backgroundView.addGestureRecognizer(gesture)
view.addSubview(backgroundView)
}
if optionView == nil
{
optionView = UIView(frame: CGRect(x: -40, y: 30, width: 100, height: 100)) // x = button.wdith / 2 - optionView.width / 2
optionView.backgroundColor = .red
navigationItem.titleView?.addSubview(optionView)
}
}
func handleGesture()
{
if optionView != nil
{
optionView.removeFromSuperview()
optionView = nil
}
if backgroundView != nil
{
backgroundView.removeFromSuperview()
backgroundView = nil
}
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}

Swift 3 add remove subview and its content dynamically

I am writing my first Swift app for quiz. Each question has random way to render on screen as below screenshot.
I am programming app without story board ie. programmatically. I want to create simple pagination flow for each question within single viewcontroller without using Collocationview, Tableview or navigation.
What I did so far? I have simple viewcontroller with UIView() added as subview. I am adding question components dynamically. Now once user click on continue I wanted remove subview and add new subview with new question. I am able to remove subview but contents on subview seems to be still there as I can see its overwriting.
To get more clarification please view my code.
import UIKit
class QuizController: UIViewController {
let subView = UIView()
var currentQuestion:Int = 1;
let questions = ["This is question 1", "Hello to question 2", "Question 3"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
setup_layout()
}
func setup_layout(){
let closeBtn = UIButton(frame: CGRect(x: 5, y: 10, width: 200, height: 50))
closeBtn.backgroundColor = UIColor.red
closeBtn.setTitle("Close", for: .normal)
closeBtn.addTarget(self, action: #selector(close), for: .touchUpInside)
view.addSubview(closeBtn)
//dynamic view
create_subview()
}
func nextQuestion(){
print("Show next")
if let viewWithTag = self.view.viewWithTag(currentQuestion) {
viewWithTag.removeFromSuperview()
currentQuestion += 1;
create_subview()
} else {
print("No!")
}
}
func create_subview(){
let heightOfView = view.frame.size.height
let widthOfView = view.frame.size.width
subView.tag = currentQuestion
subView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.2)
subView.frame = CGRect(x: 0, y: 60, width: self.view.frame.width, height: heightOfView - 60)
self.view.addSubview(subView)
let txtLabel1 = UILabel(frame: CGRect(x: 35, y: 120, width: widthOfView , height: 20))
txtLabel1.text = questions[currentQuestion-1]
txtLabel1.font = txtLabel1.font.withSize(12)
subView.addSubview(txtLabel1)
let nextBtn = UIButton(frame: CGRect(x: 5, y: 300, width: 200, height: 50))
nextBtn.backgroundColor = UIColor.red
nextBtn.setTitle("Continue", for: .normal)
nextBtn.addTarget(self, action: #selector(nextQuestion), for: .touchUpInside)
subView.addSubview(nextBtn)
}
func close(){
self.dismiss(animated: true, completion: nil)
}
}
And this is what I see which I click continue.
NOTE: Initially thought using Collocation or Table view will be more appropriate as I can set to scroll horizontally for each question and fetch questions using REST API and place to each cell. But I want to present next screen to user only once then click on continue. I guess with collectionview user can move to next screen on swipe.
Just found the answer. I assumed removing subview will also remove all components on subview by itself ie. UILable and UIButton in my case.
I have to remove them separately.
for subview in self.subView.subviews {
subview.removeFromSuperview()
}
Now i can add tag to components and remove like this:
for subview in self.subView.subviews {
if (subview.tag == 1) {
subview.removeFromSuperview()
}
}
Why are you removing superviews and adding again and again??
Simply change UILabel.text :)

Xcode 7.3.1 : Set background image to UINavigationBar and display back button

I want to set logo of app as background image to UINavigationBar and when user traverse into app it should display logo as well as back button on top of it.
Below is code that I've used :
func setNavigationBar() {
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.height
let screenSize: CGRect = UIScreen.mainScreen().bounds
let objCustomView = CustomView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: navigationBarHeight))
let objWindow = UIApplication.sharedApplication().keyWindow
objWindow?.addSubview(objCustomView)
self.navigationItem.setHidesBackButton(false, animated:true);
self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"", style:.Plain, target:nil, action:nil)
}
The issue with this is that back button goes behind the image.
How to fix this?
After refering post by #NDoc I'm getting extra space in left. Why so?
Also, the back button should be white with no back text i.e. only < arrow.
Below is code for customView :
class CustomView: UIView {
var imgLogo = UIImageView(frame:CGRectZero)
override init(frame: CGRect) {
super.init(frame: frame)
let screenSize: CGRect = UIScreen.mainScreen().bounds
imgLogo.frame = CGRectMake(0, 0, screenSize.width, 44.0)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
imgLogo.image = UIImage(named:"BoM_Logo")
self.addSubview(imgLogo)
}
}
You can display your logo in leftBarButtonItem and set the leftItemsSupplementBackButton to true to display backButton also like this.
let logoView = UIImageView(frame: CGRect(x: 0, y: 0, width: 60, height: 30))
logoView.image = UIImage(named: "Logo")
let item = UIBarButtonItem(customView: logoView)
self.navigationItem.leftBarButtonItem = item
To show the back button with your logo image set leftItemsSupplementBackButton to true
self.navigationItem.leftItemsSupplementBackButton = true
Edit:
If you want custom arrow then you need to use leftBarButtonItems and pass array of BarButtonItem and no need to set leftItemsSupplementBackButton to true like this.
let logoView = UIImageView(frame: CGRect(x: 0, y: 0, width: 60, height: 30))
logoView.image = UIImage(named: "Logo")
let logoItem = UIBarButtonItem(customView: logoView)
let btnBack = UIButton(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
btnBack.setImage(UIImage(named: "Back_Arrow"), forState: .Normal)
btnBack.addTarget(self, action: #selector(self.buttonAction(_:)), forControlEvents: .TouchUpInside)
let backItem = UIBarButtonItem(customView: btnBack)
self.navigationItem.leftBarButtonItems = [backItem, logoItem]
Note: Don't forgot to add buttonAction action method inside your viewController.
Try this in your appDelegate
let image = UIImage.init(named:"upper-bar.png")
UINavigationBar.appearance().setBackgroundImage(image,forBarMetrics:UIBarMetrics.Default)
for back button try this in the viewDidLoad() of your viewController
let image1 = UIImage(named: "go10.png") as UIImage?
let btnLeft = UIButton(type: .Custom)
btnLeft.frame = CGRectMake(0, 0, 25, 25)
btnLeft.setImage(image1,forState:UIControlState.Normal)
btnLeft.addTarget(self, action:(#selector(NameofyourViewController.backBtn(_:))),forControlEvents:UIControlEvents.TouchUpInside)
let leftBarButton = UIBarButtonItem(customView: btnLeft)
self.navigationItem.leftBarButtonItem = leftBarButton
#IBAction func backBtn(sender: UIButton)
{
self.navigationController?.popViewControllerAnimated(true)
}

addTarget not working for UIButton created in Swift

So, I've created a UIButton using Swift, and I animate it and it's superview using the following code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//create a UIView to contain buttons. Set background color to nil, so it's transparent.
var controlsView = UIView(frame: CGRect(x:0, y:0, width:400, height:400));
controlsView.alpha = 1.0
controlsView.backgroundColor = nil
//create a button and add some attributes. Self explanatory
var loginButton: UIButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
loginButton.frame = CGRectMake(20.0, 40.0, 200, 60)
loginButton.alpha = 0.0
loginButton.setTitle("Login", forState: .Normal)
loginButton.titleColorForState(.Normal)
loginButton.layer.cornerRadius = 20;
loginButton.layer.borderWidth = 1.0
loginButton.layer.borderColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), [0.827, 0.325, 0.0, 1.0])
loginButton.addTarget(self, action: "loginPressed:", forControlEvents: .TouchUpInside)
//add the button to the view created to hold it
controlsView.addSubview(loginButton)
//now test blurring the background view. Create a UIVisualEffectView to create the blur effect. New in iOS7/8
/*
var visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .Light)) as UIVisualEffectView
visualEffectView.frame = self.introImage.bounds
self.introImage.addSubview(visualEffectView)
visualEffectView.alpha = 0.0
*/
//add the controls subview to the image view already here.
self.introImage.addSubview(controlsView)
//loginButton.addTarget(self, action: "loginButtonWasPressed:",forControlEvents:.TouchUpInside)
UIView.animateWithDuration(0.7,
animations: {
loginButton.alpha = 1.0
loginButton.frame = CGRect(x: 20.0, y: 100.0, width: 200, height: 60)
//visualEffectView.alpha = 0.5
},
completion:nil)
}
That's working fine. Then I created a function which should be called on touchUpInside of the button I created above:
#IBAction func loginPressed(sender:UIButton){
var alertView = UIAlertView();
alertView.addButtonWithTitle("Ok");
alertView.title = "title";
alertView.message = "message";
alertView.show();
}
When I touch the button, the function DOES NOT fire. Can anyone help? I'm testing this on an actual device (iPhone 6 Plus/iOS 8.1.1
To add an action to a button, you need to use the following code
loginButton.addTarget(self,action:#selector(loginPressed),
forControlEvents:.TouchUpInside)
This code is working fine for me.
For more detail you can follow this [Attach parameter to button.addTarget action in Swift
Thanks.

Resources