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.
Related
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.
Below view is a tableViewController which is implemented and set the delegate and datasource methods and it works perfectly fine but I have no idea how to implement the purple view behavior on scrolling up and down.
How can I implement it?
The below code is the way I detect that view scrolled up or down. but still, no clue to implement this behavior.
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
if translation.y > 0 {
presenter?.viewScrolledDown()
} else {
presenter?.viewScrolledUp()
}
}
this question covers how to animate a backgroundColor. If the purple view is a UIView, this won't be a problem. It starts behind the tableView and then moves to the front of the tableView. You can handle this by using these functions:
view.bringSubviewToFront(purpleView)
view.sendSubviewToBack(tableView)
You will also need to animate the autoLayout constraints to move the bottom of the purple view up to the top of the tableView.
Requirement:
I should be able to scroll entire view if left view is visible or not (all components should scroll at a time if i scroll anywhere with in the view).
By clicking on show/hide left view button button it should be able to hide or show left view.
In cell (only cell not left view) there is a expand/collapse functionality (i can increase/decrease cell height)
And If i change font size in device settings app, it should be effect here also(So supporting dynamic font size)
What i have tried:
I have tried with tableview, scroll view but no luck. Finally want to try with collection view, can any one please help me out how to proceed with collection view. And in future do we get any complications if we use collection view.
Best Approach.
Your UI hierarchy should be like this.
StackView
CollectionView
TableView
Note: You can take both CollectionView or TableView but I prefer both different so that I don't need to put condition in delegates & datasource and I can manage easily. Choice is your's what you like to prefer.
Now your UI design looks like this
Green color button is used to show hide your left collectionview (you mentioned in your post).
Set your datas in CollectionView and TableView as per your requirement.
To toggle left menu, just use below one line code on greenButton action.
#IBAction func btnToggle(_ sender: Any) {
colView.isHidden = !colView.isHidden
}
For simple animation
#IBAction func btnToggle(_ sender: Any) {
UIView.animate(withDuration: 0.3) {
self.colView.isHidden = !self.colView.isHidden
}
}
Output:
Edit
You can take stackView in scrollview and turn off colview, tblView scrolling. Check below :
UI heirarchy
Additional code work
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
colView.isScrollEnabled = false
tblView.isScrollEnabled = false
colView.reloadData()
tblView.reloadData()
scrollView.contentSize = CGSize(width: self.view.frame.width,
height: max(colView.contentSize.height, tblView.contentSize.height))
stackHeight.constant = scrollView.contentSize.height
}
Note : It may cause some unexpected output (can be / can not be), so you need to take care of it.
Output:
What you need is UICollectionView with custom layout. You can achieve the effect you need using this approach.
There is tone of tutorials how to implement own custom layout.
Here is one of them
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 🙂.
I have a UITableViewController, and I'd like to make it not flash the vertical scroll bar when I go back from a push action segue on one of it's cells (popping the view controller and going back to the UITableViewController).
It seems that, if the table has many rows (mine has around 20 with 60 points height each, so bigger than the screen), when I go back, it always flashes the vertical scroll bar once to show where it is in the table. However, I don't want that to happen, but I do want to keep the scrollbar around so it shows when the user scrolls. Therefore, disabling it completely is not an option.
Is this default behavior and can I disable it temporarily?
There is a simpler solution that doesn't require avoiding using a UITableViewController subclass.
You can override viewDidAppear: as stated by http://stackoverflow.com/users/2445863/yonosoytu, but there is no need to refrain from calling [super viewDidAppear:animated]. Simply disable the vertical scrolling indicator before doing so, and then enable it back afterwards.
- (void)viewDidAppear:(BOOL)animated {
self.tableView.showsVerticalScrollIndicator = NO;
[super viewDidAppear:animated];
self.tableView.showsVerticalScrollIndicator = YES;
}
If you're using Interface Builder, you can disable the Shows Vertical Indicator option on the tableView for your UIViewController and enable it in code as shown above.
To get Cezar's answer to work for iOS10 I had to include a (sizeable) delay before re-enabling the scroll indicator. This looks a bit strange if someone tries to scroll before the second is up, so you can re-enable the scroll indicator as soon as someone scrolls.
override func viewDidAppear(_ animated: Bool) {
tableView.showsVerticalScrollIndicator = false
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.tableView.showsVerticalScrollIndicator = true
}
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !tableView.showsVerticalScrollIndicator {
tableView.showsVerticalScrollIndicator = true
}
}
Actually, on thinking about it, you don't even need the delay, just do this:
override func viewDidAppear(_ animated: Bool) {
tableView.showsVerticalScrollIndicator = false
super.viewDidAppear(animated)
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !tableView.showsVerticalScrollIndicator {
tableView.showsVerticalScrollIndicator = true
}
}
Update: Please, look at Cezar’s answer below, which gives a nice workaround without any of the drawbacks of my proposals.
According to the documentation it is a behaviour of UITableViewController:
When the table view has appeared, the controller flashes the table view’s scroll indicators. The UITableViewController class implements this in the superclass method viewDidAppear:.
So I think you have two options:
You can avoid using UITableViewController and start using a naked UIViewController. Rebuilding the functionality of UITableViewController from UIViewController is not that hard (you can follow this old article as reference).
Override viewDidAppear: and don’t call [super viewDidAppear:animated]. The problem here is that you don’t know what else does UITableViewController do when viewDidAppear: is called, so you might break something.