How to find and replace a subview from UIScrollView - ios

I have a scrollview to which I have added 4 subviews at ViewDidLoad, now during app usage on an event I want to replace one of the subview with a new view.
I am trying to add tag to subview and find it and replace but the tag seems to be not found. Also not able to find a correct way to replace a view
let subViews = self.scrollView.subviews
for subview in subViews{
if subview.tag == 1000 {
let tempView = subview
tempView = newView
}
}
I am sure above code isn't right , here code never enters inside the 'if' and when i see all the subviews in debug area i dont see the tag as well.
code from ViewDidLoad
let storyboard = UIStoryboard(name: "Main", bundle: nil)
self.overLeftVc = storyboard.instantiateViewController(withIdentifier: "settingViewController")
self.leftVc = storyboard.instantiateViewController(withIdentifier: "giftCardListViewController")
self.middleVc = storyboard.instantiateViewController(withIdentifier: "redeemViewController")
self.rightVc = storyboard.instantiateViewController(withIdentifier: "homeViewController")
self.overRightVc = storyboard.instantiateViewController(withIdentifier: "ticketListViewController")
let isShowBotView = self.config.getValue("isSystemGenerated", fromStore: "settings") as! Bool
if isShowBotView == true {
self.nextOfOverRightVc = storyboard.instantiateViewController(withIdentifier: "chatViewController")
}
else {
self.nextOfOverRightVc = storyboard.instantiateViewController(withIdentifier: "tileMainViewController")
}
self.nextOfOverRightVc.view.tag = 1000
addChildViewController(overLeftVc)
addChildViewController(leftVc)
addChildViewController(middleVertScrollVc)
addChildViewController(nextOfOverRightVc)
// addChildViewController(tileMainRightVc)
scrollView.addSubview(overLeftVc.view)
scrollView.addSubview(leftVc.view)
scrollView.addSubview(middleVertScrollVc.view)
scrollView.addSubview(nextOfOverRightVc.view)

save the subview's frame in a variable , remove that subview and insert a new subview at that Frame
for subview in subViews{
if subview.tag == 1000 {
self.frameOfViewToBeReplaced = subview.frame
subview.removeFromSuperView()
}
}
let newView = UIView(frame : self.frameOfViewToBeReplaced)

Related

Add UIViewController made via storyboard to TabBarController.setViewControllers

I am failing to understand the fundamentals of what is needed to add my HomeViewController (UIViewController) as one of the tabs in my homeTabBarController (UITabBarController) using the setViewControllers method.
I have tried initializing this and simply adding it as a param in the method. There seems to be a difference between a view controller created via storyboard and one created programmatically because when I tried adding a viewcontroller:UIViewController programmatically to the setViewControllers method, it worked fine.
My code below compiles however I get a runtime exception Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7b8491598) at the line when homeTabBarController.setViewControllers is called
`
func loadTabBar() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeViewController = storyboard.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as? HomeViewController
homeViewController!.title = "Home"
homeTabBarController.setViewControllers([homeViewController!], animated: false)
homeTabBarController.modalPresentationStyle = .fullScreen
present(homeTabBarController, animated: true)
}
`
//MARK: - Create the instances of ViewControllers
let grayViewController = HomeViewController()
let blueViewController = FirstViewController()
let brownViewController = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
//set title of the viewcontrollers
grayViewController.title = "home"
blueViewController.title = "first"
brownViewController.title = "second"
//Assigne the viewcontrollers to the TabBarViewController
self.setViewControllers([ grayViewController, blueViewController, brownViewController], animated: false)
//set system images to each tabBars
guard let items = self.tabBar.items else {
return
}
let images = ["house.fill", "star.fill", "bell.fill"]
for i in 0...2 {
items[i].image = UIImage(systemName: images[i])
}
self.tabBar.tintColor = .black
}
// You can download the project from the below github link
https://github.com/ahmetbostanciklioglu/AddingTabBarControllerProgrammatically.git

Scrolling CollectionView to left after ending should open another View Controller & same functionality on right end

I have a collection view in which 4-5 images are there & its scrolling direction is horizontal. I want to push to a view controller after the end of scrolling of both the side (i.e) left & right. If the user is on the most left image of collection view & trying to swipe towards the right again then it should push to a new View Controller & same on the right side.
I've tried this so far:-
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
for cell in self.qrCodeCollctionView.visibleCells{
let indexPaths = self.qrCodeCollctionView.indexPath(for: cell)
if indexPaths!.row == 0{
var cellInsets = UIEdgeInsets()
if #available(iOS 11.0, *) {
cellInsets = cell.safeAreaInsets
} else {
// Fallback on earlier versions
}
print(cellInsets)
}
}
}
Answers on Swift 4 UICollectionView detect end of scrolling are not according my required functionality.
I've solved it in this way:-
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
let contentOffsetX = scrollView.contentOffset.x
for cell in self.qrCodeCollctionView.visibleCells {
let indexPath = self.qrCodeCollctionView.indexPath(for: cell)
if indexPath?.row == 0{
if contentOffsetX < -50{
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
self.navigationController?.pushViewController(vc, animated: true)
}
}
else if indexPath?.row == (self.qrCodeDataArray.count - 1){
if contentOffsetX > (scrollView.contentSize.width - scrollView.bounds.width) - 20{
print("over right")
self.scrollViewDidEndDecelerating(self.qrCodeCollctionView)
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
let navController = UINavigationController(rootViewController: vc)
vc.ContactFromVC = self.currentContact
vc.currentIndexVC = self.currentIndex
vc.delegateContact = self
self.present(navController, animated: true, completion: nil)
}
}
}
}

Loading a ViewController from nib file owner

I have a design made in nib and in round about all ViewControllers I am loading that nib design.
Now as that was common footer so I managed to used it in the footer of all other view controllers.
Now what I want : I want that whenever User click on a footer it must start a new View Controller that will show what you can say "About us " view controller.
What I am doing:
// ON CLICK OF FOOTER I AM DOING
let mAboutUs = self.storyboard?.instantiateViewController(withIdentifier: "idAboutUs") as! AboutUs
mAboutUs.modalPresentationStyle = .fullScreen
self.present(mAboutUs, animated: true) {
}
but I am getting following error
Value of type 'FooterView' has no member 'storyboard'
My Understanding: I think from nib file we can not start a new ViewController, But I really do not want to do this thing in all other View controllers in which I added this nib (FooterView) as my footer view at bottom of each view controller.
Please help me!!!!
A UIView subclass doesn't contain a storyboard property it's for a vc subclass , You need
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "idAboutUs") as! AboutUs
if you want to present a vc from inisde the view FooterView then add a delegate like
weak var delegate:VCName?
When you create an instance
let ins = FooterView() // or from storyboard
ins.delegate = self
Then use
delegate?.present(mAboutUs, animated: true)
inside the view's class
You can use a delegate that all vcs conforms to but the easiest is to add this extension
extension UIViewController {
func topMostViewController() -> UIViewController {
if let presented = self.presentedViewController {
return presented.topMostViewController()
}
if let navigation = self as? UINavigationController {
return navigation.visibleViewController?.topMostViewController() ?? navigation
}
if let tab = self as? UITabBarController {
return tab.selectedViewController?.topMostViewController() ?? tab
}
return self
}
}
Then
guard let currentVC = (UIApplication.shared.delegate as! AppDelegate).window?.rootViewController.topMostViewController() else { return }
currentVC.present(mAboutUs, animated: true)
Your code should look similar to this:
let vc = UIStoryboard(name: "<Name of the storyboard that contains the About Us VC>", bundle: nil).instantiateViewController(withIdentifier: "idAboutUs") as! AboutUs
self.present(vc, animated: true) {
}

How to show different view controller on did select of collectionv view cell

I have 3 view controllers called firstvc, secondvc, thirdvc. And I have one collection view which will scroll horizontally. I have done that. And if I select any cell, it will print which index path it was. It's fine, no problem. So in my mainviewcontroller I have one collection view which will scroll horizontally. And there is one UIView called myview. Whenever I press any cell, I get its indexPath. I need to show my 3 view controllers as subviews of myview in my mainviewcontroller.
My code so far:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
if indexPath.item == 0 {
let alertStoryBoard = UIStoryboard(name: "Main", bundle: nil)
if let allCollectionViewController = alertStoryBoard.instantiateViewController(withIdentifier:"firstvc") as? firstvc {
self.contentSubView.addSubview(allCollectionViewController)
} else if indexPath.item == 1 {
} else if indexPath.item == 2 {
}
}
}
I am getting error on this line :
self.contentSubView.addSubview(allCollectionViewController)
Cannot convert value of type 'firstvc' to expected argument type 'UIView'
How do i solve this issues.
Thanks in advance !!
UPDATED:
if indexPath.item == 0 {
let alertStoryBoard = UIStoryboard(name: "Main", bundle: nil)
if let allCollectionViewController = alertStoryBoard.instantiateViewController(withIdentifier:"firstvc") as? firstvc {
self.contentSubView.addSubview(allCollectionViewController.view)
} else if indexPath.item == 1 {
let alertStoryBoard = UIStoryboard(name: "Main", bundle: nil)
if let allCollec = alertStoryBoard.instantiateViewController(withIdentifier:"secondvc") as? secondvc {
self.contentSubView.addSubview(allCollec.view)
}else if indexPath.item == 2 {
let alertStoryBoard = UIStoryboard(name: "Main", bundle: nil)
if let wController = alertStoryBoard.instantiateViewController(withIdentifier:"Thirdvc") as? Thirdvc {
self.contentSubView.addSubview(wController.view)
}
Only showing first vc class alone, not showing second and third one. Also, why i am following lk this means. When ever i press on any collection view cell, that particular class view controlelr have to show in my sub view of uiview in my mainviewcontroller
In my first view controller i placed one uiview with some background color .But that not at all showing .Its showing whitee color.That too not showing correctly
you can push present viewcontroller but you can't add viewcontroller as subview.
If you want to add as subview then you can do like,
self.contentSubView.addSubview(allCollectionViewController.view)
or push viewcontroller if you have navigation controller like
navigationController?.pushViewController(allCollectionViewController, animated: true)
or present it like
present(allCollectionViewController, animated: true) {
}
//Do this:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")
self.present(controller, animated: true, completion: nil)
//You have to present your view controller and what you are doing is adding it as a sub view.
do like
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
based on your comment remove the subviews before add on your myview
for subview in contentSubView.subviews {
subview.removeFromSuperview()
}
let alertStoryBoard = UIStoryboard(name: "Main", bundle: nil)
var controller: UIViewController!
if indexPath.item == 0 {
if let allCollectionViewController = alertStoryBoard.instantiateViewController(withIdentifier:"firstvc") as? firstvc {
controller = allCollectionViewController
}
} else if indexPath.item == 1 {
if let allCollec = alertStoryBoard.instantiateViewController(withIdentifier:"secondvc") as? secondvc {
controller = allCollec
}
}else if indexPath.item == 2 {
if let wController = alertStoryBoard.instantiateViewController(withIdentifier:"Thirdvc") as? Thirdvc {
controller = wController
}
}
addChildViewController(controller)
// Add the child's View as a subview
contentSubView.addSubview(controller.view)
controller.view.frame = contentSubView.bounds
controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// tell the childviewcontroller it's contained in it's parent
controller.didMove(toParentViewController: self)
}
The error is very obvious. You can only use addSubview according to this definition:
open func addSubview(_ view: UIView)
If you want to use multiple view controllers in one view controller, I would recommend using container view controllers.
Try this tutorial: https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/
Otherwise a simple approach is to use:
self.contentSubView.addSubview(allCollectionViewController.view)
You can then use constraints on the view to manage multiple view controllers.

Change height of UIPopoverController, to height of UITextView

In my app there is the need for a popover element, that displays the full description.
There is a normal UITextView in the UIViewController that displays the first 4 lines of the description. Now when the user clicks on that description the full description must be displayed. This will be done with the popover control recently introduced for the iPhone.
At the moment everything works, except for the fact that I want the popover to have a height that is exactly the height of the string in the UITextView in the DescriptionPopupViewController. This way if the description is longer, it will automatically make the popover higher.
The following code is called when the user clicks the UITextView:
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
if textView == descriptionTextView {
let sb = UIStoryboard(name: "Main-alternative", bundle: nil)
let popoverController = sb.instantiateViewControllerWithIdentifier("descriptionControllerID") as! DescriptionPopupViewController
popoverController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverController.popoverPresentationController?.delegate = popoverController;
popoverController.popoverPresentationController?.sourceView = descriptionTextView
popoverController.popoverPresentationController?.sourceRect = descriptionTextView.frame
popoverController.descriptionText = currentObject?["description"] as! String
if let frameHeight = popoverController.textView?.frame.height {
popoverController.preferredContentSize = CGSizeMake(fixedWidth, frameHeight)
}
self.presentViewController(popoverController, animated: true, completion: nil)
}
return false
}
So the preferredContentSize should get the height of the enclosed UITextView in the DescriptionPopupViewController. However when I try to do something like this it won't work:
I think because the frameHeight is nil
When the UIViewController is created its view hierarchy may not (and so all subviews, properties will still be nil), for optimization reasons it may not be loaded. You need to the access controller's view to force it to load its view hierarchy. So you need to update you code like the following:
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
if textView == descriptionTextView {
let sb = UIStoryboard(name: "Main-alternative", bundle: nil)
let popoverController = sb.instantiateViewControllerWithIdentifier("descriptionControllerID") as! DescriptionPopupViewController
popoverController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverController.popoverPresentationController?.delegate = popoverController;
popoverController.popoverPresentationController?.sourceView = descriptionTextView
popoverController.popoverPresentationController?.sourceRect = descriptionTextView.frame
// forces to load its view hierarchy
var view = popoverController.view
popoverController.descriptionText = currentObject?["description"] as! String
if let frameHeight = popoverController.textView?.frame.height {
popoverController.preferredContentSize = CGSizeMake(fixedWidth, frameHeight)
}
self.presentViewController(popoverController, animated: true, completion: nil)
}
return false
}
I hope this help you.

Resources