I have a table view with static cells.
My second section has a header but no rows, under that section I have a label that when pressed should call sendEmail() function which will open the email app on their device.
I've tried using a label, text view, button, overriding the didSelectCellForRow function and all have failed.
I'm completely lost what ouches aren't being recognized.
I've added print statements to my touchesBegan function but they never print.
What could be the issue?
import UIKit
import MessageUI
class InfoTVC: UITableViewController, MFMailComposeViewControllerDelegate{
let ownerEmail = "test#email.com"
#IBOutlet weak var contactLbl: UILabel!
// MARK: - View functions
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - IBOutlet methods
#IBAction func backBtn(_ sender: Any) {
_ = navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
}
// MARK: - Email methods
// Open users email app on device
func sendEmail(){
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([ownerEmail])
mail.setMessageBody("<p>Hello I had the chance to use your app and </p>", isHTML: true)
present(mail ,animated: true, completion: nil)
}
else{
// Failure
print("failed to open mail")
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self.view)
if contactLbl.frame.contains(location) {
print("yes")
sendEmail()
}
else{
print("no")
}
}
}
}
try to add tap gesture recognizer instead of handing touches
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.youLabelTapped(_:)))
yourLabel.addGestureRecognizer(tapGesture)
OR alternatively: add target directly to your label
yourLabel.addTarget(self, action: #selector(self.youLabelTapped(_:)), forControlEvents: .TouchUpInside)
and you func that will be called:
func youLabelTapped(_ sender: UITapGestureRecognizer) {
print("label tapped")
}
EDIT:
REQUIRED: don't forget to set user interaction enabled:
yourLabel.isUserInteractionEnabled = true
Related
I am created a meme generator app to better learn Swift and Xcode. I am learning to move the view when the user interacts with a text field that would be obstructed by the keyboard. I have this functionality working, with one exception. The desired functionality is to have the view slide up when the user is entering text for the bottom textfield, bot the top. The view slides up regardless of the text field the user is interacting with.
How can I assign this functionality only to the bottom text field? Here is my source code:
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate {
#IBOutlet weak var imagePickerView: UIImageView!
#IBOutlet weak var cameraButton: UIBarButtonItem!
#IBOutlet weak var topText: UITextField!
#IBOutlet weak var bottomText: UITextField!
let memeTextAttributes:[String:Any] = [
NSAttributedStringKey.strokeColor.rawValue: UIColor.black,
NSAttributedStringKey.foregroundColor.rawValue: UIColor.white,
NSAttributedStringKey.font.rawValue: UIFont(name: "HelveticaNeue-CondensedBlack", size: 30)!,
NSAttributedStringKey.strokeWidth.rawValue: -5.0]
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
subscribeToKeyboardNotifications()
}
override func viewDidLoad() {
super.viewDidLoad()
// Diable camer a button if camera ource isn't available
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
topText.delegate = self
bottomText.delegate = self
topText.textAlignment = .center
bottomText.textAlignment = .center
topText.defaultTextAttributes = memeTextAttributes
bottomText.defaultTextAttributes = memeTextAttributes
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromKeyboardNotifications()
}
// MARK: Delegate Methods
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
imagePickerView.image = image
self.dismiss(animated: true, completion: nil)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.topText.resignFirstResponder()
self.bottomText.resignFirstResponder()
return true
}
// MARK: Move the keyboard up when the bottom textfield is tapped
#objc func keyboardWillShow(_ notification:Notification) {
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue // of CGRect
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
// MARK: Move view down when keyboard is dismissed
#objc func keyboardWillHide(_ notification: Notification) {
view.frame.origin.y = 0
}
// MARK: IBActions
#IBAction func pickAnImageFromAlbum(_ sender: Any) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = .photoLibrary
present(pickerController, animated: true, completion: nil)
}
#IBAction func pickAnImageFromCamera(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}
}
You can simply try this
//make a global textField to keep reference
var currentTappedTextField : UITextField?
//use this method to get tapped textField
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
currentTappedTextField = textField
return true
}
// now move view only when textfield is bottom
#objc func keyboardWillShow(_ notification:Notification) {
if(currentTappedTextField == bottomText){
view.frame.origin.y = 0 - getKeyboardHeight(notification)
}
}
Add view in scrollview.
Use
pod 'IQKeyboardManagerSwift'
It will automatically handle that. In app delegate write this code :
IQKeyboardManager.sharedManager().enable = true
IQKeyboardManager.sharedManager().keyboardDistanceFromTextField = 30.0
If you want for one textfield:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textField == toptextField {
IQKeyboardManager.sharedManager().enable = false
}
else {
IQKeyboardManager.sharedManager().enable = true
}
return true
}
Approach without using external framework:
Use a bottom constraint from the text field to the parent view.
Adjust the constant value based on whether the keyboard is shown or hidden.
Steps:
Create a bottom constraint from your text field to the parent view.
Set the constraint's constant to an initial desired value
Add store the constraint as a property in the view controller
Observe UIKeyboardDidShow notification and get the end frame of the keyboard. Use the negative height of the end frame as the bottom constraint's constant.
Similarly do the same in UIKeyboardWillHide and set the bottom constraint constant to the original constant value
You just need to observe Keyboard Notification in your viewDidLoad :
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide),
name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
And declare selector methods to change your view constraint :
#objc
func keyboardWillShow(_ notification: Notification) {
if let keyboardHeight = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
yourViewBottomConstraint.constant = keyboardHeight.cgRectValue.height + constantHeight
UIView.animate(withDuration: 0.25, animations: {
self.view.layoutIfNeeded()
})
}
}
#objc
func keyboardWillHide() {
yourViewBottomConstraint.constant = constantHeight
UIView.animate(withDuration: 0.25, animations: {
self.view.layoutIfNeeded()
})
}
Just don't forget to implement UITextFieldDelegate :
extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
}
I have an overlay view to segregate content, I'm checking for authentication in viewWillAppear() and I have a Notification subscribed to my Auth method. If I authenticate before any of my other views appear the overlay does not show up, however it does on the first view and will not go away even after calling removeFromSuperView().
import UIKit
import FirebaseAuth
class ProtectedViewController: UIViewController, ForceSignInBannerDelegate,
SignUpViewControllerDelegate, LoginViewControllerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
NotificationCenter.default.addObserver(self, selector: #selector(checkAuthentication), name: .myNotification, object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.checkAuthentication()
}
func checkAuthentication() {
let bannerViewController = ForceSignInBanner.instanceFromNib() as! ForceSignInBanner
bannerViewController.delegate = self
if (!AuthenticationService.sharedInstance.isAuthenticated()) {
self.setView(view: bannerViewController, hidden: false)
print("Need to login")
} else if(AuthenticationService.sharedInstance.isAuthenticated()) {
self.setView(view: bannerViewController, hidden: true)
}
}
func setView(view: UIView, hidden: Bool) {
UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: { _ in
view.isHidden = hidden
if hidden {
view.removeFromSuperview()
} else {
self.view.addSubview(view)
}
}, completion: nil)
}
It's because you're trying to remove a new ForceSignInBanner each time. Ideally you should create it once and keep a reference to the ForceSignInBanner created (as an optional property of ProtectedViewController).
Then remove the ForceSignInBanner that you've stored in the property.
class ProtectedViewController: UIViewController, ForceSignInBannerDelegate {
// This lazily loads the view when the property is first used and sets the delegate.
// Ideally you wouldn't force-case the `as` but I've left it for simplicity here.
private lazy var forceSignInBannerView: ForceSignInBanner = {
let forceSignInBannerView = ForceSignInBanner.instanceFromNib() as! ForceSignInBanner
forceSignInBannerView.delegate = self
return forceSignInBannerView
}()
// ... your other code ... //
fun toggleForceSignInBannerViewVisibility(isVisible: Bool) {
if isVisible {
view.addSubview(forceSignInBannerView)
} else {
forceSignInBannerView.removeFromSuperview()
}
}
}
I have a simple MFMailComposeViewController which is presented on my existing view controller, I am trying to check if the user is doing any activities on this by detecting UITouches, But I am not able to record any UITouches on view controller, I have tried adding another view with clear color but that just disables the interaction on my mail composer , Please suggest.
Code for ViewController
import Foundation
import UIKit
import MessageUI
class ViewController: UIViewController , MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
let bounds = UIScreen.mainScreen().bounds
let width = bounds.size.width
let height = bounds.size.height
let rect = CGRect(x: 0, y: 0, width: width, height: height);
let testLayer = CustomView(frame: rect)
testLayer.backgroundColor = UIColor.clearColor()
mailComposeViewController.view.addSubview(testLayer)
}
#IBAction func sendEmailButtonTapped(sender: AnyObject) {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self // Extremely important to set the --mailComposeDelegate-- property, NOT the --delegate-- property
mailComposerVC.setToRecipients(["someone#somewhere.com"])
mailComposerVC.setSubject("Sending you an in-app e-mail...")
mailComposerVC.setMessageBody("Sending e-mail in-app is not so bad!", isHTML: false)
return mailComposerVC
}
func showSendMailErrorAlert() {
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// MARK: MFMailComposeViewControllerDelegate Method
func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("inside mail composer view")
}
}
code for CustomView
import UIKit
class CustomView: UIView {
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("touches started")
}
}
I am trying to exclude a subview from my TapGestureRecognizer.
It has been created in sotryboard, the delegate is connected to the controller.
In my ViewControler I have the GestureRecognizerDelegate protocol and I ve set my gesture.delegate = self.
Though the shouldReceiveTouch Gesturerecognizer function is not calling, any idea ?
Here is somne of the code :
class DetailedPostViewController: UIViewController, UITextViewDelegate, MKMapViewDelegate, UIGestureRecognizerDelegate {
let circularLike = CircularLike(frame: CGRectZero)
#IBOutlet var gesture: UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
gesture.delegate = self
self.view.addSubview(circularLike)
circularLike.frame = self.view.bounds
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view!.isDescendantOfView(circularLike){
return false
}
return true
}
#IBAction func UserTap(sender: AnyObject) {
if fromUser {
dismissViewControllerAnimated(true, completion: nil)
} else {
performSegueWithIdentifier("userPage", sender: nil)
}
}
#IBAction func hideUnhide(sender: UIGestureRecognizer) {
if hide {
hide=false
unhideUi()
} else {
hide = true
hideUi()
}
}
}
ok, In fact, I just had to set my subview userTouchEnabled parameter to false, and it then triggered the ShouldRecieveTouch Function ...
Right now I am trying to make a leaderboard I created show up. The player is authenticated just fine, but when the game center window opens it is very strange. Here is a picture:
Here is the code I am using to display this image:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.showLeaderboard()
}
func showLeaderboard() {
var leaderView = UIViewController()
var leaderViewController = GKGameCenterViewController(rootViewController: leaderView)
leaderViewController.viewState = GKGameCenterViewControllerState.Leaderboards
leaderViewController.leaderboardIdentifier = "High_Score_Board"
self.showViewController(leaderViewController, sender: self)
//self.presentViewController(leaderViewController, animated: true, completion: nil)
}
func leaderboardViewControllerDidFinish(controller: GKGameCenterViewController){
controller.dismissViewControllerAnimated(true, completion: nil)
}
All of this is in my GameViewController. Also, even if this works, how would I access this method in my SKScenes? Thanks for the help!
Import GameKit:
import GameKit
Make sure to add the GKGameCenterControllerDelegate delegate within your class.
class ViewController: UIViewController, GKGameCenterControllerDelegate {
...
}
That delegate requires a method which is called when the player taps on the "Done" button.
func gameCenterViewControllerDidFinish(gcViewController: GKGameCenterViewController!)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
This is the function that includes the code needed to display the leaderboard:
func showLeaderboard() {
var gcViewController: GKGameCenterViewController = GKGameCenterViewController()
gcViewController.gameCenterDelegate = self
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
// Remember to replace "Best Score" with your Leaderboard ID (which you have created in iTunes Connect)
gcViewController.leaderboardIdentifier = "Best_Score"
self.showViewController(gcViewController, sender: self)
self.navigationController?.pushViewController(gcViewController, animated: true)
// self.presentViewController(gcViewController, animated: true, completion: nil)
}
You can now call showLeaderboard by pressing a button:
#IBAction func buttonShowLeaderboard(sender: AnyObject) {
showLeaderboard()
}