I am trying to get all views inside my UIViewController.
I am using this code:
for subview : UIView in self.view.subviews{
//code here
}
the problem is that I don't get views that are inside a UIView.
so I get all view except of their children.
please, how can I get ALL Views that are either child or parent inside my UIViewController? even if a view has a child that has a child that has a child. I want to go through all views.
thanx in advance :)
(please in swift 4.0)
You need to recursively process all subviews.
func processSubviews(of view: UIView) {
// 1. code here do something with view
for subview in view.subviews {
// 2. code here do something with subview
processSubviews(of: subview)
// 3. code here do something with subview
}
// 4. code here do something with view
}
You need to put your code at either position 1, 2, 3, or 4 (or possibly two or more of those places) depending on the results you want.
Then you can call this as:
processSubviews(of: self.view)
For get all views (as Label, Button, View, ...) in your view hierarchy and access to all child view in your viewController you should using from a recursive func to iterate all views :
func getAllSubviews(view:UIView) {
for subview in view.subviews {
if let takenLabel = subview as? UILabel {
takenLabel.backgroundColor = UIColor.red
}
getAllSubviews(view:subview)
}
}
You can using from this func to access other view like button and change them .
in this case i'm changing background color of all label in viewController to red
There are many different way to get subviews
To Get all the subviews in the parent view
for subview in view.subviews{
// you can get all of your views one by one here.
}
To Filter if any tagged view is available or not (lets assume view is tagged by 100)
view.subviews.filter{$0.tag == 100}
Extensions to check if Any view contains sub views
extension UIView{
func isAvailabletoParentView(inParentView : UIView, subViewTag : Int) -> Bool{
var isAvailable = false
for subviews in inParentView.subviews{
if subviews.tag == subViewTag{
isAvailable = true
return true
}else{
isAvailable = false
}
}
return isAvailable
}
}
Call Extension like
subview.isAvailabletoParentView(inParentView: parentView, subViewTag: anyTagToSubView)
Hope it will helps , Happy coding 🙂.
Related
I have a table view that has a UIImage and some UIButton objects in each TableView cell. When I scroll the table view, it works quite well overall. However, if I touch one of the UIButton items to scroll the table view, the UIButton seems to steal the touches and the table view does not scroll. Instead the UIButton items appears to be selected instead. I would like to be able to scroll the table view even when the user touches buttons when starting to scroll. So, I searched for solutions here, tried the following.
extension UITableView {
override public func touchesShouldCancel(in view: UIView) -> Bool {
print("the touchesShouldCancel function is called.")
if view is UIButton {
return true
}
return super.touchesShouldCancel(in: view)
}
}
However, it doesn't work. The function does not even get called whenever I scroll the table view. What am I missing here? I would greatly appreciate your input. Thanks all.
Subclass UITableView Set tableView canCancelContentTouches to true as per Apple docs
The scroll view does not call this method if the value of the
canCancelContentTouches property is false
class YourTableView:UITableView {
override func awakeFromNib() {
canCancelContentTouches = true
delaysContentTouches = false
}
override func touchesShouldCancel(in view: UIView) -> Bool {
}
}
You need to make a UITableView subclass
class SubTbl:UITableView {
// add your method
}
Then assign it to that table in IB or use it in code
I have a UIView that has another UIView as a subview, I want to animate these views when the user performs a specific action, I want to do this using the snapshotView method.
The problem is that I am unable to snapshot only the main view without the other one being a part of it, and I can't seem to access the subview either to transform it. So my question is, can I snapshot a UIView, without its subviews, or can I access a subview through a snapshot?
Here is simplified demo of possible approach (hide all subviews before snapshot and show right after done)
extension UIView {
func snapshotMe() -> UIView? {
_ = self.subviews.compactMap { $0.isHidden = true }
defer {
_ = self.subviews.compactMap { $0.isHidden = false }
}
return self.snapshotView(afterScreenUpdates: true)
}
}
of course if your view subviews might be in already mixed hidden/visible state then in provided above extension you have to filter visible only subview in advance.
Once you snapshot a view, it’s essentially just a flattened image. So as a workaround, you could remove the subview from your view, snapshot the view, put the subview back. Or you can snapshot your subview. Or you could do both.
I'm working on my view and I'm having an issue with getting a shadow around a button within the stack view. Most of the work I have done has been within the storyboard directly.
Here is the method I am using to apply the shadow to the view
func addShadow(to view: UIView) {
view.layer.shadowColor = shadowColor
view.layer.shadowOpacity = shadowOpacity
view.layer.shadowOffset = shadowOffset
if let bounds = view.subviews.first?.bounds {
view.layer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
view.layer.shouldRasterize = true
}
and this is how I'm finding the button within the view from ViewController.swift
for subview in self.view.subviews {
if subview.isKind(of: UIButton.self) && subview.tag == 1 {
addShadow(to: subview)
}
}
I know the problem stems from the stack view and the UIView inside of the stack view that holds the button. (self.view > UIStackView > UIView > [UIButton, UILabel])
I know I could do this with recursion in the for-loop but I'm trying to be a little more precise to optimize performance and would prefer to add the shadows in one shot.
You have a few options:
add the shadow in the storyboard itself
add an outlet to the button, then add shadow in code
add the button to a collection, then enumerate over the collection adding shadows
recursively add the shadows (this isn't going to hit performance nearly as hard as you're thinking, adding the shadows hurts performance more than doing this recursively)
You are correct in that the button is a view on the stack view, so your for loop doesn't hit the button directly to add a shadow to it.
The easiest way to solve this is by far the recursive way, or something like this:
func addShadowsTo(subviews: [UIView]) {
for subview in subviews {
if subview.isKind(of: UIButton.self) && subview.tag == 1 {
addShadow(to: subview)
}
if let stackView = subview as? UIStackView {
addShadowToSubviews(subviews: stackView.subviews)
}
}
}
func viewDidload() {
super.viewDidLoad()
addShadowsTo(subviews: view.subviews)
}
If you want some instructions on how to do any of the other ways, just comment.
Sorry about the title I didn't know how to say it properly. I have been playing around with the view controllers within a scrollview concept (like Snapchat) in this tutorial:
https://www.youtube.com/watch?v=eHERMrAcmdo
The problem I'm having is that when I add navigation to the view controllers I want to be able to hide stuff on the scrollview. I basically added some overlay buttons and I want to hide them when i'm not in those 3 root controllers. There have been a lot of different things I have wanted to change in the scrollview from within the other view controllers but I just don't understand how to access it.
If I understand you correctly, it sounds like you are trying to detect which view the user is on and have some logic that shows and hides some buttons. So if you want your logic to do this within the main ViewController class you could do something like this..
add UIScrollViewDelegate to your ViewController
such as
class ViewController: UIViewController, UIScrollViewDelegate
then in the viewDidLoad()
scrollView.delegate = self
then add this function under the viewDidLoad()
func scrollViewDidScroll(scrollView: UIScrollView) {
let offSet = self.scrollView.contentOffset
if offSet.x == 0 {
print("On scrollview 1") // Hide the button here?
} else if offSet.x == 414.0 {
print("On scrollview 2")
} else if offSet.x == 828.0 {
print("On scrollview 3")
}
}
Okay I have figured it out. This question is also asked somewhere else and I think he ended up doing it a different way but this is the simplest way and probably the best one. The 3 view controllers are childViewControllers of the scrollview, so I had to use self.parentViewController to access the scrollview.
I have many views and for each views, many subviews.
I tried to specify for each subviews
scrollsToTop = false
but my TableView doesn't scroll to top when I press the status bar.
I suppose I missed some views...
I know there is a similar post but it doesn't answer (UITableView scroll to top when tapping status bar at top of the screen)
So I decided to implement this function:
override func viewDidLoad() {
super.viewDidLoad()
checkForScrollViewInView(self.view)
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.scrollsToTop = true
}
func checkForScrollViewInView(view:UIView) {
for subview in view.subviews as [UIView] {
if subview.isKindOfClass(UITextView) {
(subview as! UITextView).scrollsToTop = false
}
if subview.isKindOfClass(UIScrollView) {
(subview as! UIScrollView).scrollsToTop = false
}
if subview.isKindOfClass(UITableView) {
(subview as! UITableView).scrollsToTop = false
}
if (subview.subviews.count > 0) {
self.checkForScrollViewInView(subview)
}
}
}
But it doesn't work too. I don't know what I miss.
You may stuggle with 2 problems:
You have more than one instance of UIScrollView. You should disable scrollsToTop (eg. self.collectionView?.scrollsToTop = false in viewDidLoad) for the parent UIScrollView or objects that inherit from it (like UITableView or UICollectionView).
From Apple scrollViewShouldScrollToTop(_:)
If the delegate doesn’t implement this method, true is assumed. For the scroll-to-top gesture (a tap on the status bar) to be effective, the scrollsToTop property of the UIScrollView must be set to YES.
If your hierarchy is highly customized method scrollViewShouldScrollToTop won't be called. You should check why does happen. Otherwise you will have to some dirty work: add some gestures and handle them.
Interesting links:
Scroll to top of UITableView by tapping status bar
Why doesn't UIScrollView/UITableview respond to taps on the status bar, and scroll to the top?
https://developer.apple.com/library/prerelease/ios//documentation/UIKit/Reference/UIScrollViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIScrollViewDelegate/scrollViewShouldScrollToTop:
Ok I found why my tableview wouldn't scroll!
I called my checkForScrollViewInView() function only in the viewDidLoad of my table view's view controller. In order to disable scrollToTop for every child views.
But I forgot that I had another "active" view controller... In my app I have a left menu which opens to swipe. Calling checkForScrollViewInView() in the left menu's view controller solves my issue.
func checkForScrollViewInView(view:UIView) {
for subview in view.subviews as [UIView] {
if subview.isKindOfClass(UITextView) {
(subview as! UITextView).scrollsToTop = false
}
if subview.isKindOfClass(UIScrollView) {
(subview as! UIScrollView).scrollsToTop = false
}
if subview.isKindOfClass(UITableView) {
(subview as! UITableView).scrollsToTop = false
}
if (subview.subviews.count > 0) {
self.checkForScrollViewInView(subview)
}
}
}