I'm new to iOS programming. I'm developing a custom keyboard with a custom nib file for it's view. How can I add target for buttons in the nib file without connect them manually to my UIInputViewController?
I tried this code but doesn't works!
class LatinKeyboard: UIInputViewController {
override func viewDidLoad() {
super.viewDidLoad();
let nib = UINib(nibName: "KeyboardView", bundle: nil);
let objects = nib.instantiateWithOwner(self, options: nil)
view = objects[0] as UIView;
for subview in view.subviews {
if(subview is UIButton){
var key = subview as UIButton;
key.addTarget(self, action: "keyPressed:", forControlEvents: .TouchUpInside);
}
}
}
func keyPressed(sender: AnyObject?) {
let button = sender as UIButton
let title = button.titleForState(.Normal)
(textDocumentProxy as UIKeyInput).insertText(title!)
}
}
Related
Code for the Custom UIView:
Please check the video too here: https://drive.google.com/open?id=1kbrOxXWcJIi4vkiqMNer3exBr5cOWgDz
import UIKit
protocol PostAttachmentFullScreenViewDelegate: class {
func closeAttachmentFullView()
}
class PostAttachmentFullScreenView: UIView {
weak var delegate: PostAttachmentFullScreenViewDelegate?
#IBOutlet var backgroundView: UIImageView!
#IBOutlet var closeButton: UIButton!
#IBAction func closeViewAction(_ sender: Any) {
print("will call delegate to put it off")
self.delegate?.closeAttachmentFullView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let _ = commonInitialization()
backgroundView.image = UIImage(named: "ScrollImageTop1")
closeButton.isUserInteractionEnabled = true
}
override init(frame: CGRect) {
super.init(frame: frame)
let _ = commonInitialization()
backgroundView.image = UIImage(named: "ScrollImageTop1")
closeButton.isUserInteractionEnabled = true
}
func commonInitialization() -> UIView
{
let bundle = Bundle.init(for: type(of: self))
let nib = UINib(nibName: "PostAttachmentFullScreenView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
return view
}
}
usage in ViewController (I am defining an instance of the custom view and putting it inside the Scroll View):
var frame = CGRect(x:0, y:0, width:0, height:0)
let blue = PostAttachmentFullScreenView()
blue.delegate = self
blue.isUserInteractionEnabled = true
blue.backgroundColor = UIColor.blue
blue.backgroundView.image = fileAttachments[1]
frame.origin.x = attachmentsScrollView.frame.size.width * CGFloat (0)
frame.size = attachmentsScrollView.frame.size
blue.frame = frame
attachmentsScrollView.addSubview(blue)
extension NewPostViewController : PostAttachmentFullScreenViewDelegate
{
func closeAttachmentFullView() {
print("hiding attachments view")
attachmentSuperView.isHidden = true
}
}
To my surprise it doesn't even print - "will call delegate to put it off".
I am not able to understand what's wrong here. Please help me understand the issue and correct it. Thank you.
You are mixing programmatic approach and xib approach.
As you have added IBOultet and IBAction that means you are using xib for the UIView.
In that scenario you have to load the UIView xib when initialising the view.
Add an extension for UIView in your project:
extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}
when you are initialising your view add it like this :
let blue : PostAttachmentFullScreenView = UIView.fromNib()
blue.delegate = self
blue.isUserInteractionEnabled = true
blue.backgroundColor = UIColor.blue
blue.backgroundView.image = fileAttachments[1]
frame.origin.x = attachmentsScrollView.frame.size.width * CGFloat (0)
frame.size = attachmentsScrollView.frame.size
blue.frame = frame
attachmentsScrollView.addSubview(blue)
and the delegate and button action methods will work.
you missed this :
You never set the target/action on your button. Somewhere you need to call addTarget(_:action:for:) to set the target/action on the button. Also, what connects the button to your PostAttachmentFullScreenView as an outlet?
This might be an obvious one but for me (Xcode 10.1) adding all missing UI constraints to the UIButton in question (at least 4 constraints) fixed the error for me in my custom view:
Make sure you add enough constraints (typically 4 constraints) or enough to have all warnings regarding missing constraints removed. After doing this and attaching the button with Ctrl + drag from View to corresponding swift code, the click was being detected and working properly.
Hope this helps.
I have 1 MainViewController from storyboard and 1 ModalUIView from xib.
In ModalUIView has present function for displaying modal and dismiss function for closing modal.
Step:
MainViewController -> OpenModal -> ModalUIView -> CloseModal
Here are my code:
UIViewUtil.swift
import Foundation
import UIKit
extension UIView {
// Load xib as the same name of CustomView that want to use xib
func loadXib() -> UIView{
let bundle = Bundle(for: type(of: self))
let nibName = type(of: self).description().components(separatedBy: ".").last!
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as! UIView
}
}
MainViewController.swift is subclass of UIViewController
#IBAction func guideButton(_ sender: Any) {
let modal = ModalUIView()
modal.present(targetView: self.view)
}
ModalUIView.swift is subclass of UIView
var view : UIView?
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
setup()
}
func setup() {
view = loadXib()
}
func present(targetView: UIView) {
view!.layer.cornerRadius = 10.0
view!.clipsToBounds = true
targetView.addSubview(view!)
// Set size
let popupWidth: CGFloat = targetView.frame.width - (targetView.frame.width * 0.04)
let popupHeight: CGFloat = targetView.frame.height - (targetView.frame.height * 0.08)
view!.frame = CGRect(x: targetView.frame.origin.x, y: targetView.frame.origin.y,
width: popupWidth, height: popupHeight)
view!.center = targetView.center
view!.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
view!.alpha = 0
UIView.animate(withDuration: 0.4){
self.view!.alpha = 1
self.view!.transform = CGAffineTransform.identity
}
}
#objc func dismiss(sender: UIButton!) {
print("dismiss")
}
My problem is mainViewController when I called present of modalUIView and then I tab on closeButton in modalUIView is not fired the action
I try with #IBAction but it not work:
#IBAction func CloseButtonAction(_ sender: UIButton) {
}
I also try with manual add action by programmatically but it not work too:
let closeButton: UIButton? = view?.viewWithTag(10) as! UIButton
closeButton!.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
Note:
I can see and tap on closeButton on modal.
I already add ModalUIView to xib File's Owner
OK, I have a solution now but not sure that is the best answer.
My solution is to add an action to closeButton of modalUIView via mainViewController not in modalUIView
If anyone have another best solution than this please suggest me.
You can also get an action in your viewcontroller following.
yourSubView.button.addTarget(self, action: #selector(buttonPress(sender:)), for: .touchUpInside)
and it will call your method.
func buttonPress(sender:UIButton){
print("Button pressed")
}
I am using a custom titleView and assigning it to navigationItem titleView. It had been working fine until iOS 11. Since the update it's position got misplaced to center as originally it was on more left. Beside that user interaction is not working.
titleView = Bundle.main.loadNibNamed("SomeNib", owner: self, options: nil)?.first as? SomeNib
navigationItem.titleView = titleView
titleView is just a usual nib.
then for enabling interaction:
if let titleView = self.navigationItem.titleView {
let tap = UITapGestureRecognizer(target: self, action: #selector(onTitleViewTap))
titleView.addGestureRecognizer(tap)
titleView.isUserInteractionEnabled = true
}
In iOS 11, titleView is getting set with Autolayout. Hence, the size of the titleView is the intrinsic size of the view you are setting in titleView.
This code in your view class(which you are setting as titleView) should help:
override var intrinsicContentSize: CGSize {
return UILayoutFittingExpandedSize
}
I have a created a Custom UIView with xib. Inside the UIView, I added UILabel, ImageView and a UIButton on top. On click on Hidden button I am able to call the click event.
import UIKit
class NavBarTitleView: UIView {
#IBOutlet weak var title : UILabel!
#IBOutlet weak var clickButton: UIButton!
/// Create an instance of the class from its .xib
class func instanceFromNib() -> NavBarTitleView {
return UINib(nibName: "NavBarTitleView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! NavBarTitleView
}
//this line of code will help to enable a click event.
override var intrinsicContentSize: CGSize {
return UIView.layoutFittingExpandedSize
}
}
Then in UIViewcontroler, I added the below code.
if let title = self.navBarTitle{
if navbarTitleView == nil {
navbarTitleView = NavBarTitleView.instanceFromNib()
self.navigationItem.titleView = navbarTitleView
}
navbarTitleView!.title.text = title
navbarTitleView!.clickButton.addTarget(self, action: #selector(didTapNavbarTitle(_:)), for: .touchUpInside)
navbarTitleView!.layoutIfNeeded()
}else{
navigationItem.title = ""
}
Button action:
#objc func didTapNavbarTitle(_ sender: Any){
print("NavBar Selected")
}
Result -
I hope this will work. Tested on code 11.6 and os version 13.6
I have a common functionality of displaying a corner settings list in almost all pages of my application. I have thought of using extension to achieve this common functionality.
My Code -
I created a NSObject subclass and within it had an extension of UIViewController -
import UIKit
class viewControllerExtension: NSObject
{
}
extension UIViewController
{
func setCornerSettingsTableView()
{
let maskView = UIView()
maskView.frame = self.view.bounds
maskView.backgroundColor = UIColor.lightGrayColor()
let tableView = self.storyboard?.instantiateViewControllerWithIdentifier("cornerSettingVC") as! cornerSettingVC
addChildViewController(tableView)
tableView.view.frame = CGRectMake(maskView.frame.width-maskView.frame.width/2.5, 0,self.view.frame.width/2.5, self.view.frame.height-200)
maskView.addSubview(tableView.view)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissMaskView")
maskView.addGestureRecognizer(tap)
self.view.addSubview(maskView)
}
func dismissMaskView()
{
print("dismiss called")//function called but how to dismiss the mask View
}
}
Usage - In any view controller where I need to display i just call - setCornerSettingsTableView()
Problem - As you can see I am trying to add a tap gesture recognizer to my mask view so that whenever user taps the mask view, it removes the mask view along with the table view in it, but I am unable to achieve that.
If any alternative suggestions for this are most welcome.
extension UIViewController
{
func setCornerSettingsTableView()
{
if let theMask = self.view.viewWithTag(666) as? UIView {
return // already setted..
} else {
let maskView = UIView()
maskView.tag = 666
maskView.frame = self.view.bounds
maskView.backgroundColor = UIColor.lightGrayColor()
let tableView = self.storyboard?.instantiateViewControllerWithIdentifier("cornerSettingVC") as! cornerSettingVC
addChildViewController(tableView)
tableView.view.frame = CGRectMake(maskView.frame.width-maskView.frame.width/2.5, 0,self.view.frame.width/2.5, self.view.frame.height-200)
maskView.addSubview(tableView.view)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissMaskView")
maskView.addGestureRecognizer(tap)
self.view.addSubview(maskView)
}
}
func dismissMaskView()
{
print("dismiss called")//function called but how to dismiss the mask View
if let theMask = self.view.viewWithTag(666) as? UIView {
theMask.removeFromSuperview()
}
}
}
how could I get the frame of a rightbarbuttonItem in swift? I found this : UIBarButtonItem: How can I find its frame? but it says cannot convert NSString to UIView, or cannot convert NSString to String :
let str : NSString = "view" //or type String
let theView : UIView = self.navigationItem.rightBarButtonItem!.valueForKey("view")//or 'str'
The goal is to remove the rightBarButtonItem, add an imageView instead, and move it with a fadeOut effect.
Thanks
You should try it like:
var barButtonItem = self.navigationItem.rightBarButtonItem!
var buttonItemView = barButtonItem.valueForKey("view")
var buttonItemSize = buttonItemView?.size
Edit (Swift 3):
var barButtonItem = self.navigationItem.rightBarButtonItem!
let buttonItemView = barButtonItem.value(forKey: "view") as? UIView
var buttonItemSize = buttonItemView?.size
In Swift4, XCode9
for view in (self.navigationController?.navigationBar.subviews)! {
if let findClass = NSClassFromString("_UINavigationBarContentView") {
if view.isKind(of: findClass) {
if let barView = self.navigationItem.rightBarButtonItem?.value(forKey: "view") as? UIView {
let barFrame = barView.frame
let rect = barView.convert(barFrame, to: view)
}
}
}
}
import UIKit
class ViewController: UIViewController {
let customButton = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
customButton.setImage(UIImage(named: "myImage"), for: .normal)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: customButton)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(self.customButton.convert(self.customButton.frame, to: nil))
}
}
You must use UIBarButtonItem objects created with customView for this; UIBarButtonItem objects created the regular way don't have enough information exposed. It's important that the frame be looked up after the parent UINavigationController's UINavigationBar has completely laid out its entire subview tree. For most use cases, the visible UIViewController's viewDidAppear is a good enough approximation of this.