Swift4 ViewController not fired event in subView - ios

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")
}

Related

Show WebView ViewController through the xib View

I have a xib view and a button in it but when i try to configure it to present a ViewController with a WebView it shows some errors.
the xib file:
class popUpView: UIView, UITextFieldDelegate {
#IBOutlet weak var payNowPopUp: UIButton!
#IBOutlet weak var priceTextFieldPopUp: UITextField!
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup(frame:CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
}
func xibSetup(frame: CGRect){
let view = loadXib()
view.frame = frame
addSubview(view)
popUpViewInterface.layer.cornerRadius = 10
}
func loadXib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "popUpView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as? UIView
return view!
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
priceTextFieldPopUp.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
priceTextFieldPopUp.resignFirstResponder()
return true
}
#IBAction func payNowToWebView(_ sender: Any) {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "paymentWebView")
else {
return
}
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
}
Inside the #IBAction func payNowToWebView(_ sender: Any) { } I want to present a viewController when the button is clicked but it's showing some errors like: Value of type 'popUpView' has no member 'storyboard' and Value of type 'popUpView' has no member 'present'
Do it like this (Remember to replace 'Main' with your storyboard name):
#IBAction func payNowToWebView() {
let storyboard: UIStoryboard = UIStoryboard (name: "Main", bundle: nil)
let vc: paymentWebView = storyboard.instantiateViewControllerWithIdentifier("paymentWebView") as! PaymentWebView
let currentController = self.getRootVC()
currentController?.presentViewController(vc, animated: false, completion: nil)
}
func getRootVC() -> UIViewController? {
if let rootController = UIApplication.shared.keyWindow?.rootViewController {
var currentController: UIViewController! = rootController
while( currentController.presentedViewController != nil ) {
currentController = currentController.presentedViewController
}
return currentController
}
return nil
}
present is a method and storyboard is a property both of them inside a UIViewController instance not a UIView instance
You can replace self.storyboard with UIStoryboard(name:"YourName",nil)but for present you need a vc to show the needed one from it most commonly to have a delegate weak variable inside that view that references the vc where the view is inside or you may get the app's window root vc then vc.present(.....

Delegate in UIView

I present modally UIViewController with workout. When user dismiss this VC I need to hold unfinished workout and present small UIView with size exactly the same as UITabBar in all others view controllers. This UIView has a button which should instantiate unfinished workout. The problem is that delegate doesn't work.
class BackToWorkoutButton: UIView {
weak var delegate: BackButtonWorkoutDelegate?
var routine: Routine!
init(frame: CGRect, routine: Routine) {
self.routine = routine
super.init(frame: frame)
customInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func customInit() {
let xibView = Bundle.main.loadNibNamed("BackToWorkoutButton", owner: self, options: nil)?.first as! UIView
xibView.frame = self.bounds
addSubview(xibView)
}
#IBAction func backButtonTapped(_ sender: UIButton) {
// THIS DELEGATE DOESN'T WORK
delegate?.backToWorkout()
}
}
class WorkoutCoordinator {
func instantiateUnfinishedWorkout() {
let window = UIApplication.shared.keyWindow!
guard let tabBar = navigationController.tabBarController else { return print("Tabbar nil") }
let height = tabBar.tabBar.frame.height
let child = NewWorkoutCoordinator(routine: temporaryRoutine, navigationController: navigationController, removeCoordinatorWith: removeChild, workoutViewBarProtocol: self)
let view2 = BackToWorkoutButton(frame: CGRect(x: 0, y: (tabBar.tabBar.frame.origin.y) - height, width: window.frame.width, height: height), coordinator: child, routine: routine)
view2.delegate = self
tabBar.view.addSubview(view2)
}
}

Adding UIView with data to the window

I need to add a UIView with size of tabbar but exactly above tabbar. This view allow user to come back to a started workout. My idea is holding data inside UIView and instantiate a View Controller with unfinished data when user clicks button. The problem is that when I want to instantiate VC my data in UIView become nil.
class BeforeRoutineClass {
// HERE I CREATE the UIView
func showWorkoutView(temporaryRoutine: Routine) {
guard let tabBar = navigationController.tabBarController else { return print("Tabbar is nil") }
let window = UIApplication.shared.keyWindow!
let height = tabBar.tabBar.frame.height
view2 = BackToWorkoutButton(frame: CGRect(x: 0, y: (tabBar.tabBar.frame.origin.y) - height, width: window.frame.width, height: height), routine: temporaryRoutine)
window.addSubview(view2!)
}
}
class BackToWorkoutButton: UIView {
var routine: Routine?
init(frame: CGRect, routine: Routine?, bobo: String?) {
self.routine = routine
super.init(frame: frame)
customInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func customInit() {
let xibView = Bundle.main.loadNibNamed("BackToWorkoutButton", owner: self, options: nil)?.first as! UIView
xibView.frame = self.bounds
addSubview(xibView)
}
#IBAction func backButtonTapped(_ sender: UIButton) {
// THERE IS NIL ALWAYS
guard let routine = routine else { return print("Routine is nil") }
let routineVC = RoutineFactory.startWorkoutScene(routine: routine)
let navBarOnModal = UINavigationController(rootViewController: routineVC)
navBarOnModal.modalPresentationStyle = .fullScreen
guard let nav = UIApplication.shared.windows.last?.rootViewController else { return print("there is no nav")}
nav.present(navBarOnModal, animated: true, completion: nil)
self.removeFromSuperview()
}
}

Button click not working for custom UIView

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.

Swift, iboutlet and custom controls

I may be doing something really stupid, but I don't seem to be able to use Interface Builder to connect IBOutlet variables to custom views, but only in Swift.
I've created a class called MyView, which extends from UIView. In my controller, I've got a MyView variable (declared as #IBOutlet var newView: MyView). I go into IB and drag a UIView onto the window and give it a class of MyView.
Whenever I've done similar in Objective C, I'm then able to click on the View Controller button at the top of the app window, select the variable and drag it down to the control to link the two together. When I try it in Swift, it refuses to recognise that the view is there.
If I change the class of the variable in the controller to UIView, it works fine. But not with my custom view.
Has anyone else got this problem? And is it a feature, or just my idiocy?
Code for Controller
import UIKit
class ViewController: UIViewController {
#IBOutlet var newView:MyView
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Code for view
import UIKit
class MyView: UIView {
init(frame: CGRect) {
super.init(frame: frame)
// Initialization code
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect)
{
// Drawing code
}
*/
}
I've had a similar problem, and I think it's partially a caching issue and partially just an Xcode6/Swift issue. The first step I found was required was to make sure that the view controller .swift file would be loaded in the Assistant Editor when choosing "automatic".
With Xcode finding that both the files are linked I could sometimes control-drag from the view/button/etc. from the IB to the .swift file, but often had to drag from the empty circle in the gutter of the #IBOutlet var newView:MyView line to the view I wanted it to match up to.
If you can't get the file to load in the Assistant Editor then I found that doing the following would often work:
Remove the custom class from the IB view
Clean the project (cmd + K)
Close/reopen Xcode
Possibly clean again?
Add the custom class back to the view
Hope it works :)
If that seems to get you half way/nowhere add a comment and I'll see if it triggers anything else I did
In my case import UIKit was missing, after adding this line I could create an IBOutlet from Storyboard again.
I've had a similar problem to the one described in this thread. Maybe you found a solution maybe not but anybody who encounters this in the future. I've found the key is to use the "required init" function as follows:
required init(coder aDecoder: NSCoder) {
print("DrawerView: required init")
super.init(coder: aDecoder)!
screenSize = UIScreen.mainScreen().bounds
screenWidth = screenSize.width
screenHeight = screenSize.height
self.userInteractionEnabled = true
addCustomGestureRecognizer()
}
This is the complete class of my custom view:
import UIKit
import Foundation
class DrawerView: UIView {
var screenSize: CGRect!
var screenWidth: CGFloat!
var screenHeight: CGFloat!
var drawerState: Int = 0
override init (frame : CGRect) {
print("DrawerView: main init")
super.init(frame : frame)
}
override func layoutSubviews() {
print("DrawerView: layoutSubviews")
super.layoutSubviews()
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
print("DrawerView: required init")
super.init(coder: aDecoder)!
screenSize = UIScreen.mainScreen().bounds
screenWidth = screenSize.width
screenHeight = screenSize.height
self.userInteractionEnabled = true
addCustomGestureRecognizer()
}
func addCustomGestureRecognizer (){
print("DrawerView: addCustomGestureRecognizer")
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(self.handleDrawerSwipeGesture(_:)))
swipeDown.direction = UISwipeGestureRecognizerDirection.Down
self.addGestureRecognizer(swipeDown)
let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(self.handleDrawerSwipeGesture(_:)))
swipeUp.direction = UISwipeGestureRecognizerDirection.Up
self.addGestureRecognizer(swipeUp)
print("DrawerView self: \(self)")
}
func minimizeDrawer(){
UIView.animateWithDuration(0.25, delay: 0.0, options: .CurveEaseOut, animations: {
// let height = self.bookButton.frame.size.height
// let newPosY = (self.screenHeight-64)*0.89
// print("newPosY: \(newPosY)")
self.setY(self.screenHeight*0.86)
}, completion: { finished in
self.drawerState = 0
for view in self.subviews {
if let _ = view as? UIButton {
let currentButton = view as! UIButton
currentButton.highlighted = false
} else if let _ = view as? UILabel {
let currentButton = view as! UILabel
if self.tag == 99 {
currentButton.text = "hisotry"
} else if self.tag == 999 {
currentButton.text = "results"
}
}
}
})
}
func handleDrawerSwipeGesture(gesture: UIGestureRecognizer) {
print("handleDrawerSwipeGesture: \(self.drawerState)")
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch self.drawerState{
case 0:
if swipeGesture.direction == UISwipeGestureRecognizerDirection.Down {
// nothing to be done, mini and swiping down
print("mini: !")
} else {
// mini and swiping up, should go to underneath city box
UIView.animateWithDuration(0.25, delay: 0.0, options: .CurveEaseOut, animations: {
let toYPos:CGFloat = 128 + 64 + 8
self.setY(toYPos)
}, completion: { finished in
self.drawerState = 1
for view in self.subviews {
if let _ = view as? UIButton {
let currentButton = view as! UIButton
currentButton.highlighted = true
} else if let _ = view as? UILabel {
let currentLabel = view as! UILabel
currentLabel.text = "close"
}
}
})
}
break;
case 1:
if swipeGesture.direction == UISwipeGestureRecognizerDirection.Down {
// open and swiping down
self.minimizeDrawer()
} else {
// open and swiping up, nothing to be done
}
break;
default:
break;
}
}
}
}
Hope this helps...

Resources