AddSubview to ScrollView - swift

I am trying to add MultipleAnswerView to the main VC scrollview if the picker's first value is selected. But so far it does not add or do anything to VC. the value of picker is working correctly and I can go through .addSubview but after this line, it does not go inside MultipleAnswerView
class AddQuestionViewController: BaseViewController {
var pickerView = UIPickerView()
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
self.setupDefaultViews()
}
extension AddQuestionViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
surveyTypeTextField.text = surveyTypes[row]
surveyTypeTextField.resignFirstResponder()
setupDefaultViews()
setupViews()
}
}
private func setupViews() {
if surveyTypeTextField.text == surveyTypes[0] {
if let view = multipleAnswerView as MultipleAnswerView? {
view.parentVC = self
self.scrollView.addSubview(view)
}
}
}
This is MultipleAnswerView which I try to add to VC with .addSubview
class MultipleAnswerView: UIView, SurveyAnswerViewDelegate, UITextFieldDelegate {
var parentVC: AddQuestionViewController!
public var delegate: SurveyAnswerViewDelegate?
#IBOutlet weak var answerLabel: UILabel!
public var nib = UINib(nibName: "MultipleAnswerView", bundle: Bundle.main)
override func awakeFromNib() {
setupMultipleAnswerView()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
public convenience init(nib: UINib) {
self.init()
self.nib = nib
setupMultipleAnswerView()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
//manually set subview frames here
}
func setupMultipleAnswerView() {
self.answerLabel.label(textStr: "Cevaplar",...
}

Related

How to navigate push view controller from UIView in Swift

How to navigate PushViewController from UIView class to New View Controller, Till now what I tried with delegate/ protocol but still no success from button click, below is the swift code what I tried to navigate from uiview class
protocol MyViewDelegate {
func didTapButton()
}
class Ticket : UIView {
static let instance = Ticket()
#IBOutlet var parentView: UIView!
#IBOutlet weak var ticketView: UIView!
var delegate : MyViewDelegate?
override init (frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("Ticket", owner: self, options: nil)
}
#IBAction func btnHistoryTicket(_ sender: ThemeButtonTicket) {
}
#IBAction func btnNewTicket(_ sender: ThemeButtonTicket) {
func buttonTapAction() {
delegate?.didTapButton() // Expect to navigate
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showTicket(){
UIApplication.shared.windows.first{$0.isKeyWindow}?.addSubview(parentView)
}
}
class ViewController: UIViewController, MyViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let ticket = Ticket()
ticket.delegate = self
}
func didTapButton() {
let vc = kMainStoryboard.instantiateViewController(withIdentifier: "SubmitTicketVC") as! SubmitTicketVC
self.navigationController?.pushViewController(vc, animated: true)
}
}

Call to IBOutlet in custom UIView from another class

I am looking for a way to refer to the IBOutlet variable in a custom class UIView, from another class. I found layoutSubviews, but each change only works on the first call, and not on each subsequent call. Thanks for help!
ViewController class:
var SB = StatusBar()
SB.update(1)
SB.update(2)
SB.update(3)
StatusBar class:
class StatusBar: UIView {
#IBOutlet var view: UIView!
#IBOutlet weak var label: UILabel!
var ActualStatus: Int!
required init?(coder: NSCoder) {
super.init(coder: coder)
xibSetup()
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
func update(status) {
ActualStatus = status
self.layoutSubviews()
}
override func layoutSubviews() {
super.layoutSubviews()
label.text = ActualStatus
}
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of:self))
let nib = UINib(nibName: "StatusBar", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
}
Result: label.text is 1, change only works on the first call

Passing data from UIViewController to Custom UIView with XIB

I have the following swift file which controls a xib file
import UIKit
protocol SelectProfile: class {
func selectionUp(id: Int, selected: Bool)
}
class Component: UIView {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var selection: UIView!
var selected: Bool = false
var profileId: Int = 6
weak var delegate: SelectProfile?
#IBAction func selProfile(_ sender: Any) {
selected = !selected
selection.isHidden = !selected
delegate?.selectionUp(id: profileId, selected: selected)
}
let nibName = "Component"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
selection.isHidden = true
name.text = String(profileId)
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
In my UIViewController where I want to interact with the component I have the following code (shortened for brevity)
import UIKit
class Home: UIViewController, SelectProfile {
#IBOutlet weak var profileComponent: Component!
func selectionUp(id: Int, selected: Bool) {
print(id, selected)
}
override func viewDidLoad() {
profileComponent.delegate = self
profileComponent.profileId = 2
}
}
My delegate works nicely and I'm able to change the values for IBOutlets but I can't pass a value to variables. This line doesn't pass the data to my xib profileComponent.profileId = 2.
It seems like I have an issue with initialization of the xib but I don't know how to fix it.
The viewDidLoad of controller is called after view's init, so when you change profileId as you do, it is changed, but not reflected anywhere.
I assume the following, at least, missed:
var profileId: Int = 6 {
didSet {
name.text = String(profileId)
// call delegate here if needed as well
}
}

Where to set UiTextField delegate method in custom UiView

Error occurs when I set UITextField delegate.
My code is:
import UIKit
class UserAlertVC: UIView , UITextFieldDelegate {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.addBehavior()
}
func addBehavior (){
print("Add all the behavior here")
userNameTxtField.delegate = self
passwordTxtField.delegate = self
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
return true
}
func textFieldDidBeginEditing(textField: UITextField) {
}
#available(tvOS 10.0, *)
func textFieldDidEndEditing(textField: UITextField, reason: UITextFieldDidEndEditingReason) {
}
#IBAction func actionOnCancel(sender: UIButton) {
self .removeFromSuperview()
}
#IBAction func actionOnProceed(sender: UIButton) {
self .removeFromSuperview()
UserAlertVC.showAlertForUser()
}
#IBOutlet var userNameTxtField: UITextField!
#IBOutlet var passwordTxtField: UITextField!
static func showAlertForUser() {
let alert = NSBundle.mainBundle().loadNibNamed("KeyboardViewController", owner: self, options: nil)!.last as! UIView
let windows = UIApplication.sharedApplication().windows
let lastWindow = windows.last
alert.frame = UIScreen.mainScreen().bounds
lastWindow?.addSubview(alert)
}
}
Error message is:
fatal error: unexpectedly found nil while unwrapping an Optional value
I have used Custom Alert View using XIB.pls suggest any solution.
Firstly take a look at life cycle of the view. Depending on this it is possible to highlight that method awakeFromNib is quite suitable because:
The nib-loading infrastructure sends an awakeFromNib message to each
object recreated from a nib archive, but only after all the objects in
the archive have been loaded and initialized. When an object receives
an awakeFromNib message, it is guaranteed to have all its outlet and
action connections already established.
Make sure to put an #IBOutlet for the .Xib content view, also you need to add the Nib code. Last, make sure in your ViewController you set your UIView Outlet to be UserAlertVC and you add the awakeFromNib method. Please find attached the code. Let me know if you need further help.
Here is the code related to the .xib file.
import UIKit
class UserAlertVC: UIView, UITextFieldDelegate {
//MARK: - Outlets
#IBOutlet var contentView: UIView!
#IBOutlet var userNameTxtField: UITextField!
#IBOutlet var passwordTxtField: UITextField!
//MARK: - Loads
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
commonInit()
}
//MARK: - Functions
func commonInit() {
Bundle.main.loadNibNamed("UserAlertVC", owner: self, options: nil)
userNameTxtField.delegate = self
passwordTxtField.delegate = self
contentView.translatesAutoresizingMaskIntoConstraints = false
addSubview(contentView)
// add constraints programmatically
}
// add the rest of your code
}
Here is the code related to the ViewController.
class ViewController: UIViewController {
//MARK: - Outlets
#IBOutlet weak var userAlertVC: UserAlertVC!
//MARK: - Loads
override func viewDidLoad() {
super.viewDidLoad()
}
override func awakeFromNib() {
super.awakeFromNib()
}
}

In Swift, how do I have a UIScrollView subclass that has an internal and external delegate?

I'm subclassing UIScrollView to add some features such as double tap to zoom and an image property for gallery purposes. But in order to do the image part my subclass has to be its own delegate and implement the viewForZoomingInScrollView.
But then when someone uses my scroll view subclass, they might like to get delegate notifications as well to see scrollViewDidScroll or what have you.
In Swift, how do I get both of these?
Here is a Swift version of this pattern:
Although forwardInvocation: is disabled in Swift, we can still use forwardingTargetForSelector:
class MyScrollView: UIScrollView {
class _DelegateProxy: NSObject, UIScrollViewDelegate {
weak var _userDelegate: UIScrollViewDelegate?
override func respondsToSelector(aSelector: Selector) -> Bool {
return super.respondsToSelector(aSelector) || _userDelegate?.respondsToSelector(aSelector) == true
}
override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? {
if _userDelegate?.respondsToSelector(aSelector) == true {
return _userDelegate
}
else {
return super.forwardingTargetForSelector(aSelector)
}
}
func viewForZoomingInScrollView(scrollView: MyScrollView) -> UIView? {
return scrollView.viewForZooming()
}
// Just a demo. You don't need this.
func scrollViewDidScroll(scrollView: MyScrollView) {
scrollView.didScroll()
_userDelegate?.scrollViewDidScroll?(scrollView)
}
}
private var _delegateProxy = _DelegateProxy()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.delegate = _delegateProxy
}
override init(frame: CGRect) {
super.init(frame: frame)
super.delegate = _delegateProxy
}
override var delegate:UIScrollViewDelegate? {
get {
return _delegateProxy._userDelegate
}
set {
self._delegateProxy._userDelegate = newValue;
/* It seems, we don't need this anymore.
super.delegate = nil
super.delegate = _delegateProxy
*/
}
}
func viewForZooming() -> UIView? {
println("self viewForZooming")
return self.subviews.first as? UIView // whatever
}
func didScroll() {
println("self didScroll")
}
}
Here's a simple working Playground version in Swift 3 that acts purely as an observer rather than only as an interceptor like the other answers here.
The distinction is that the original scroll view delegate should have all of its delegate methods called like normal versus them being hijacked by another delegate.
(You can copy/paste this into a playground and run it to test)
import UIKit
final class ScrollViewObserver: NSObject, UIScrollViewDelegate {
// MARK: - Instantiation
init(scrollView: UIScrollView) {
super.init()
self.scrollView = scrollView
self.originalScrollDelegate = scrollView.delegate
scrollView.delegate = self
}
deinit {
self.remove()
}
// MARK: - API
/// Removes ourselves as an observer, resetting the scroll view's original delegate
func remove() {
self.scrollView?.delegate = self.originalScrollDelegate
}
// MARK: - Private Properties
fileprivate weak var scrollView: UIScrollView?
fileprivate weak var originalScrollDelegate: UIScrollViewDelegate?
// MARK: - Forwarding Delegates
/// Note: we forward all delegate calls here since Swift does not support forwardInvocation: or NSProxy
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Run any custom logic or send any notifications here
print("proxy did scroll")
// Then, forward the call to the original delegate
self.originalScrollDelegate?.scrollViewDidScroll?(scrollView)
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
self.originalScrollDelegate?.scrollViewDidZoom?(scrollView)
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.originalScrollDelegate?.scrollViewWillBeginDragging?(scrollView)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
self.originalScrollDelegate?.scrollViewWillEndDragging?(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
self.originalScrollDelegate?.scrollViewDidEndDragging?(scrollView, willDecelerate: decelerate)
}
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
self.originalScrollDelegate?.scrollViewWillBeginDecelerating?(scrollView)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.originalScrollDelegate?.scrollViewDidEndDecelerating?(scrollView)
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
self.originalScrollDelegate?.scrollViewDidEndScrollingAnimation?(scrollView)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.originalScrollDelegate?.viewForZooming?(in: scrollView)
}
func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
self.originalScrollDelegate?.scrollViewWillBeginZooming?(scrollView, with: view)
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
self.originalScrollDelegate?.scrollViewDidEndZooming?(scrollView, with: view, atScale: scale)
}
func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
return self.originalScrollDelegate?.scrollViewShouldScrollToTop?(scrollView) == true
}
func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
self.originalScrollDelegate?.scrollViewDidScrollToTop?(scrollView)
}
}
final class TestView: UIView, UIScrollViewDelegate {
let scrollView = UIScrollView()
fileprivate(set) var scrollObserver: ScrollViewObserver?
required init() {
super.init(frame: .zero)
self.scrollView.delegate = self
self.scrollObserver = ScrollViewObserver(scrollView: self.scrollView)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("view's original did scroll delegate method called")
}
}
let testView = TestView()
testView.scrollView.setContentOffset(CGPoint(x: 0, y: 100), animated: true)
testView.scrollObserver?.remove()
print("removed the observer")
testView.scrollView.setContentOffset(CGPoint(x: 0, y: 200), animated: true)
testView.scrollView.setContentOffset(CGPoint(x: 0, y: 300), animated: true)
This prints
proxy did scroll
view's original did scroll delegate method called
removed the observer
view's original did scroll delegate method called
view's original did scroll delegate method called
Swift 4+ version of rintaro's excellent answer:
class MyScrollView: UIScrollView {
class _DelegateProxy: NSObject, UIScrollViewDelegate {
weak var _userDelegate: UIScrollViewDelegate?
override func responds(to aSelector: Selector!) -> Bool {
return super.responds(to: aSelector) || _userDelegate?.responds(to: aSelector) == true
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
if _userDelegate?.responds(to: aSelector) == true {
return _userDelegate
}
return super.forwardingTarget(for: aSelector)
}
//This function is just a demonstration, it can be replaced/removed.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
(scrollView as? MyScrollView)?.didScroll()
_userDelegate?.scrollViewDidScroll?(scrollView)
}
}
fileprivate let _delegateProxy = _DelegateProxy()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.delegate = _delegateProxy
}
override init(frame: CGRect) {
super.init(frame: frame)
super.delegate = _delegateProxy
}
override var delegate: UIScrollViewDelegate? {
get {
return _delegateProxy._userDelegate
}
set {
_delegateProxy._userDelegate = newValue
}
}
func didScroll() {
print("didScroll")
}
}
I don't know about any 100% Swift solution for this.
Taking this ObjC answer to the same problem, and trying to port it to Swift it turns out that is not possible since NSInvocation is not available in Swift.
What we can do is to implement the suggested MyScrollViewPrivateDelegate in ObjC(don't forget to import it in the bridging header file) and the scroll view subclass in Swift like the following:
MyScrollView.swift
import UIKit
class MyScrollView: UIScrollView {
private let myDelegate = MyScrollViewPrivateDelegate()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
super.delegate = myDelegate
}
override init(frame: CGRect) {
super.init(frame: frame)
super.delegate = myDelegate
}
override var delegate: UIScrollViewDelegate? {
set {
myDelegate.userDelegate = newValue
super.delegate = nil
super.delegate = myDelegate
}
get {
return myDelegate.userDelegate
}
}
func viewForZooming() -> UIView {
return UIView()// return whatever you want here...
}
}
MyScrollViewPrivateDelegate.h
#import <UIKit/UIKit.h>
#interface MyScrollViewPrivateDelegate : NSObject <UIScrollViewDelegate>
#property (weak, nonatomic) id<UIScrollViewDelegate> userDelegate;
#end
MyScrollViewPrivateDelegate.m
#import "MyScrollViewPrivateDelegate.h"
#import "YOUR_MODULE-Swift.h"
#implementation MyScrollViewPrivateDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
// you could check if the user delegate responds to viewForZoomingInScrollView and call it instead...
return [(MyScrollView *)scrollView viewForZooming];
}
- (BOOL)respondsToSelector:(SEL)selector
{
return [_userDelegate respondsToSelector:selector] || [super respondsToSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:_userDelegate];
}
#end