How to iterate through all UIButtons on screen in Swift 3.0? - ios

I am trying to do a loop to iterate through all UIButtons found on the view in order to change the position of each button. (See code below)
for case let button as UIButton in self.view.subviews {
rect = button.frame
rect.origin.x = rect.origin.x + 20
rect.origin.y = rect.origin.y + 20
button.frame = rect
}
However, when I debugged the code I found that the loop is not finding any buttons, and thus the positions remained the same. I have 9 buttons in a View found in the View Controller.
Anyone knows what's wrong with this loop?

Your code works fine for me. The implication is that your buttons are not subviews of self.view, but subviews of some view further down the hierarchy. You will have to write a recursive version of your loop.
For example:
func lookForButton(_ v: UIView) {
let subs = v.subviews
if subs.count == 0 {return}
for vv in subs {
if vv is UIButton {
print(vv) // just testing: do your real stuff here
}
lookForButton(vv)
}
}
lookForButton(self.view)

Your code is working. This:
var view = UIView()
view.addSubview(UIView())
view.addSubview(UIButton())
for case let button as UIButton in view.subviews {
print(button)
}
prints:
<UIButton: 0x7fcc9bd07320; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x60000002dca0>>
Conclusion: self.view.subviews does not contain any UIButtons.

Related

Display a subview in the centre of the display screen of i phone or ipad

I am designing an alert view in which I display a custom view when user taps a particular button.I am using a scroll view for my lengthy content view. I have written the code for the same as given below.
I want that this alert view to display right in the middle of your display screen irrespective of scroll position or content view position as alert in iOS by default pop ups. So is there any way to do that. I have tried a way but didn't succeed.
And the only solution I found is to scroll to top before displaying and add the subview. And since this is not perfect,so please suggest some measures or suggestions. Thanks.
#IBAction func forgotpassword(sender: AnyObject)
{
blurEffect = UIBlurEffect(style: .Dark)
blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = self.contentview.bounds
blurEffectView.autoresizingMask = [.FlexibleWidth,.FlexibleHeight]
self.AlertVieww.frame.size = CGSizeMake(blurEffectView.frame.width-40, self.AlertVieww.frame.height)//AlertVieww is the IBOutlet of my custom view in scene dock
AlertVieww.center = CGPointMake(UIScreen.mainScreen().bounds.size.width/2, UIScreen.mainScreen().bounds.size.height/2);
AlertVieww.autoresizingMask = [.FlexibleHeight,.FlexibleWidth]
blurEffectView.addSubview(self.AlertVieww)
[self.scrollview.setContentOffset(CGPointZero, animated: false)]//I scroll to the top
UIView.transitionWithView(self.view, duration: 1.0, options: UIViewAnimationOptions.TransitionFlipFromBottom, animations:
{
self.contentview.addSubview(self.blurEffectView)
}, completion: { finished in
self.scrollview.scrollEnabled = false
})
}
//These are two cnacel buttons in which I remove the views
#IBAction func sendAlert(sender: AnyObject)
{
self.blurEffectView.hidden = true
self.scrollview.scrollEnabled = true
}
#IBAction func cancelAlert(sender: AnyObject)
{
self.blurEffectView.hidden = true
self.scrollview.scrollEnabled = true
}
try with that
add your alert view in topmost controller's view instead of blurEffectView and set center of alertview isequal to topmost controller's view's center
like ,
AlertVieww.center = UIApplication.sharedApplication().keyWindow?.rootViewController?.view.center
UIApplication.sharedApplication().keyWindow?.rootViewController?.view.addSubview(self.AlertVieww)
UIView *subview = your View To Be Centered In Its SuperView;
UIView *superView = subview.superview;
subview.center = [superView convertPoint:superView.center
fromView:superView.superview];
If view is nil(on fromView:), this method instead converts from window base coordinates. Otherwise, both view and the receiver must belong to the same UIWindow object.
NOTE: If you use the auto layout stuff, then you have to change the constraints . not the frame or center
Here is a solution
Instead of self.view.frame.size.width
Use:: [UIScreen mainScreen].bounds.size.width or height
So code is
UIView *View = [[UIView alloc] init];
View.frame = CGRectMake([UIScreen mainScreen].bounds.size.width / 2, [UIScreen mainScreen].bounds.size.height / 2 , 18, 36);
[self.view addSubview: View];
here try it this one also to make cGpoint and according your main view:
view.center = CGPointMake(self.view.frame.size.width / 2,
self.view.frame.size.height / 2);
try this one...!
self.view.center = CGPointMake(CGRectGetMidX(self.parentView.bounds),
CGRectGetMidY(self.parentView.bounds));
remove this code
AlertVieww.center = CGPointMake(UIScreen.mainScreen().bounds.size.width/2, UIScreen.mainScreen().bounds.size.height/2);
Add this code
AlertVieww.center = self.blurEffectView.center
after
self.contentview.addSubview(self.blurEffectView)
One way is to create your own AlertController(inherits from UIViewController) and in its xib, put all the views for your alert along with proper constraint to be correctly shown on iPhone/iPad and whenever you need to show, Just present it on window like the following .
[window.rootViewController presentViewController:yourAlertControllerInstance animated:YES completion:nil];
OR
You can use this custom alert . It has variety of style along with BlurView in background and you can customise it as per your need.

How do I make a UIImage fade in and fade out as I scroll?

I have a stretchy header that I made following this tutorial http://blog.matthewcheok.com/design-teardown-stretchy-headers/. Anyway it's working perfectly but I'm having trouble making a UIView on top of it fade out as the view stretched and returning to original alpha as the view is returned to normal. Best I could come up with is this:
override func
scrollViewDidScroll(scrollView: UIScrollView) {
updateHeaderView()
var offset = scrollView.contentOffset.y
if offset < -170 {
headerBlurImageView?.alpha = max(0.0, offset - 95/35)
} else {
self.headerBlurImageView?.alpha = 1
}
}
But it barely works. There is no smooth transition between the alphas and when the view is returned to normal the alpha doesn't return. Any advice or hints?
Update: I managed to do the exact opposite of what I wanted :p Here's the code:
override func scrollViewDidScroll(scrollView: UIScrollView) {
updateHeaderView()
var height: CGFloat
var position: CGFloat
var percent: CGFloat
height = scrollView.bounds.size.height/2
position = max(-scrollView.contentOffset.y, 0.0)
percent = min(position / height, 1.0)
self.headerBlurImageView.alpha = percent
}
Take a look at the UIScrollViewDelegate protocol. there are a number of messages that relate to starting and ending dragging, and decelerating. You should be able to set your view controller up as the scroll view's delegate and implement some of those methods. I'd create a UIView animation that animates the header's alpha down when scrolling begins, and another UIView animation that animates it back to opaque once scrolling ends (or possibly once deceleration ends.)
Layout two UIImageViews on top of one another (in this example, bg is under bgB), then layout the UIScrollview at the very top. In the scrollviewDidScroll method, place the following code:
float off = cv.contentOffset.x;
float floatedIndex = off/cv.frame.size.width;
int left = floatedIndex;
int right = ceil(floatedIndex);
if (right>events.count-1){ right=events.count-1; }
float alpha = floatedIndex-left;
UIImage * leftImage = nil;
UIImage * rightImage = nil;
NSDictionary * ld = events[left];
NSDictionary * rd = events[right];
NSObject * o = ld[#"artwork"];
if ([o isKindOfClass:[UIImage class]]){
leftImage = ld[#"artwork"];
}
o = rd[#"artwork"];
if ([o isKindOfClass:[UIImage class]]){
rightImage = rd[#"artwork"];
}
bg.image = leftImage;
bgB.image = rightImage;
bgB.alpha = alpha;
The 'left' and 'right' ints correspond to indices in an array. There's a line checking that the scrollview cannot accidently go 'too far right' ie trying to find an index outside the bounds of the array.
Then I'm pulling data from an array called events, and checking for the existence of an image in the resulting dictionary (but you could use it for a straight up array of images), updating both UIImageviews and then fading the one on top (bgB) in and out depending on the scroll's content offset.
The 'animation' correlates with user-interaction, which is great, but sometimes it's better to use animation blocks with another scrollview delegate method.

Xamarin UITableView animation starts from wrong position

In my View I have one UILabel and one UITableView.
I want them to animate downwards by 40 pixels. The following is my code
var newFrame = newsStand.Frame;
newFrame = new CGRect (newsStand.Frame.X, newsStand.Frame.Y+40, newsStand.Frame.Width, newsStand.Frame.Height);
UIView.BeginAnimations ("slideAnimation");
UIView.SetAnimationDuration (2);
UIView.SetAnimationCurve (UIViewAnimationCurve.EaseInOut);
UIView.SetAnimationDelegate (this);
UIView.SetAnimationDidStopSelector (new Selector ("slideAnimationFinished"));
nfloat newY=searchLabel.Center.Y+40;
searchLabel.Center = new CGPoint (searchLabel.Center.X, newY);
newsStand.Frame = newFrame;
UIView.CommitAnimations ();
newsStand is my UITableView instance and searchLabel, UILabel instance. When this code is executed, searchLabel animates as expected by moving down 40 pixels. But newsStand jumps 40 pixels upwards initially and animates to its original position. I have tried setting center (like I have done for searchLabel) of newsStand instead of Frame also. But the result is same. What is causing this behaviour ?
Your approach is very complex. You can greatly simplify the animation by using UIView.Animate() instead:
var myLabel = new UILabel (new CGRect (100, 100, 100, 40))
{
Text = "Animate me!",
TextColor = UIColor.Yellow
};
window.Add (myLabel);
UIView.Animate (
duration: 0.5,
animation: () => myLabel.Center = new CGPoint (myLabel.Center.X, myLabel.Center.Y + 40),
completion: () => {}
);

Scrolling UICollectionView causes UITableView in parent UIView to push Collection View out of Frame

My app has a small UIView that hides behind the UINavigationBar. I have a gesture recognizer set up that when the nav bar is tapped, this 'popover' UIView drops down into view, and the UITableView below it drops down to make room for it. This works well, but when I try to swipe through the collection view to load new cells, the table view pushes the lowered view back up behind the nav bar.
I have tried to put and lldb watchpoint on the popover view and table view to see if I could track the view frame change, but I could never seem to hit the watchpoint:
(lldb) w s v self->_drawerView->_layer
Watchpoint created: Watchpoint 3: addr = 0x7feac410b788 size = 8 state = enabled type = w
watchpoint spec = 'self->_drawerView->_layer'
new value: 0x00007feac410b850
(lldb) w s v self->_tableView->_layer
Watchpoint created: Watchpoint 2: addr = 0x7feac6008008 size = 8 state = enabled type = w
watchpoint spec = 'self->_tableView->_layer'
new value: 0x00007feac4102cf0
Here is my animation code. I have put in breakpoints and know that this code is not causing this bug.
- (void)toggleDrawer:(UIGestureRecognizer *)recognizer {
[UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:0.6f initialSpringVelocity:0.5f options:0 animations:^{
if (lowerDrawer) {
self.drawerView.frame = CGRectMake(self.drawerView.frame.origin.x, self.drawerView.frame.origin.y + 70, self.drawerView.frame.size.width, self.drawerView.frame.size.height);
// lower table at same time
self.tableView.frame = CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y + 70, self.tableView.frame.size.width, self.tableView.frame.size.height - 70);
// flip arrow
self.arrowImageView.transform = CGAffineTransformMakeRotation(M_PI);
lowerDrawer = NO;
} else {
self.drawerView.frame = CGRectMake(self.drawerView.frame.origin.x, self.drawerView.frame.origin.y - 70, self.drawerView.frame.size.width, self.drawerView.frame.size.height);
self.tableView.frame = CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y - 70, self.tableView.frame.size.width, self.tableView.frame.size.height + 70);
// flip arrow
self.arrowImageView.transform = CGAffineTransformMakeRotation(M_PI * 180);
lowerDrawer = YES;
}
} completion:^(BOOL finished) {
//
}];
}
How should I even begin to debug this? Is there anything I can do to insure a collection view does not change its frame when loading new cells?
Looks like this is a conflict between the coordinates manually set by you and the coordinates automatically set by the Storyboard file.
Try checking for any auto-constraints you may have, or uncheck "Use Autolayout" in your Storyboard Interface Builder.

Continuous vertical scrolling between UICollectionView nested in UIScrollView

While I know nested scrollViews aren't ideal, our designers provided me with this setup, so I'm doing my best to make it work. Let's begin!
View Hierarchy
UIView
UIScrollView (Vertical Scrolling Only)
UIImageView
UICollectionView #1 (Horizontal Scrolling Only)
UIImageView (different from previous UIImageView)
UICollectionView #2 (Vertical Scrolling Only)
Important Note
All my views are defined using programmatic Auto Layout. Each successive view in the UIScrollView's subview hierarchy has a y-coordinate dependency on the view that came before it.
The Problem
For the sake of simplicity, let's modify the nomenclature a bit:
_outerScrollView will refer to UIScrollView
_innerScrollView will refer to UICollectionView #2
I'd like for my _outerScrollView to route its touch event to the _innerScrollView upon reaching the bottom of its contentSize. I'd like the reverse to happen when I scroll back up.
At present, I have the following code:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat bottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);
if (bottomEdge >= [_outerScrollView contentSize].height) {
_outerScrollView.scrollEnabled = NO;
_innerScrollView.scrollEnabled = YES;
} else {
_outerScrollView.scrollEnabled = YES;
_innerScrollView.scrollEnabled = NO;
}
}
where the initial conditions (before any scrolling occurs) is set to:
outerScrollView.scrollEnabled = YES;
innerScrollView.scrollEnabled = NO;
What happens?
Upon touching the view, the outerScrollView scrolls until its bottom edge, and then has a rubber band effect due to _outerScrollView.bounces = YES; If I touch the view again, the innerScrollView scroll until it hits its bottom edge. On the way back up, the same rubber banding effect occurs in the reverse order. What I want to happen is have a fluid motion between the two subviews.
Obviously, this is due to the scrollEnabled conditions that are set in the conditional in the code snippet. What I'm trying to figure out is how to route the speed/velocity of one scrollView to the next scrollView upon hitting an edge.
Any assistance in this issue would be greatly appreciated.
Other Notes
This did not work for me: https://github.com/ole/OLEContainerScrollView
I am considering putting everything in the UIScrollView hierarchy (except for UICollectionView #2) inside UICollectionView #2 supplementaryView. Not sure if that would work.
Figured it out!
First:
_scrollView.pagingEnabled = YES;
Second:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == _scrollView || scrollView == _offersCollectionView) {
CGFloat offersCollectionViewPosition = _offersCollectionView.contentOffset.y;
CGFloat scrollViewBottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);
if (scrollViewBottomEdge >= [_scrollView contentSize].height) {
_scrollView.scrollEnabled = NO;
_offersCollectionView.scrollEnabled = YES;
} else if (offersCollectionViewPosition <= 0.0f && [_offersCollectionView isScrollEnabled]) {
[_scrollView scrollRectToVisible:[_scrollView frame] animated:YES];
_scrollView.scrollEnabled = YES;
_offersCollectionView.scrollEnabled = NO;
}
}
}
Where:
_scrollView is the _outerScrollView
_offersCollectionView is the _innerScrollView (which was UICollectionView #2 in my original post).
Here's what happens now:
When I swipe up (so the view moves down), the offersCollectionView takes over the entire view, moving the other subViews out of the view.
If I swipe down (so the views up), the rest of the subviews come back into focus with the scrollView's bounce effect.
Accepted answer didn't work for me. Here's what did:
Define a subclass of UIScrollView:
class CollaborativeScrollView: UIScrollView, UIGestureRecognizerDelegate {
var lastContentOffset: CGPoint = CGPoint(x: 0, y: 0)
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer.view is CollaborativeScrollView
}
}
Since it's not possible to reroute touches to another view, the only way to ensure that the outer scrollview can continue scrolling once the inner one stops is if it had been receiving the touches the whole time. However, in order to prevent the outer one from moving while the inner one is, we have to lock it without setting its isScrollEnabled to false, otherwise it'll stop receiving the touches and won't be able to pick up where the inner one left off when we want to scroll past the inner one's top or bottom.
That's done by assigning a UIScrollViewDelegate to the scrollviews, and implementing scrollViewDidScroll(_:) as shown:
class YourViewController: UIViewController, UIScrollViewDelegate {
private var mLockOuterScrollView = false
#IBOutlet var mOuterScrollView: CollaborativeScrollView!
#IBOutlet var mInnerScrollView: CollaborativeScrollView!
enum Direction {
case none, left, right, up, down
}
func viewDidLoad() {
mOuterScrollView.delegate = self
mInnerScrollView.delegate = self
mInnerScrollView.bounces = false
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView is CollaborativeScrollView else {return}
let csv = scrollView as! CollaborativeScrollView
//determine direction of scrolling
var directionTemp: Direction?
if csv.lastContentOffset.y > csv.contentOffset.y {
directionTemp = .up
} else if csv.lastContentOffset.y < csv.contentOffset.y {
directionTemp = .down
}
guard let direction = directionTemp else {return}
//lock outer scrollview if necessary
if csv === mInnerScrollView {
let isAlreadyAllTheWayDown = (mInnerScrollView.contentOffset.y + mInnerScrollView.frame.size.height) == mInnerScrollView.contentSize.height
let isAlreadyAllTheWayUp = mInnerScrollView.contentOffset.y == 0
if (direction == .down && isAlreadyAllTheWayDown) || (direction == .up && isAlreadyAllTheWayUp) {
mLockOuterScrollView = false
} else {
mLockOuterScrollView = true
}
} else if mLockOuterScrollView {
mOuterScrollView.contentOffset = mOuterScrollView.lastContentOffset
}
csv.lastContentOffset = csv.contentOffset
}
}
And that's it. This will stop your outer scrollview from scrolling when you begin scrolling the inner one, and get it to pick up again when the inner one is scrolled all the way to one of its ends.

Resources