Programmatically added subview doesn't interact with other views, but xib-added view worked fine - ios

harlanhaskins wrote:
I have a view called userSlider programmaticaly added in viewDidLoad:
self.userSlider = [[UserSliderView alloc] initWithFrame:CGRectMake(0, -120, 320, 155)];
[self.userSlider setUser:user];
[self.userSlider configureView];
[self.view addSubview:self.userSlider];
And a tableview called gameTableView.
I originally had added the userSlider into the xib as a custom view (because UserSlider has its own xib), and when I implemented these touch methods:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = (UITouch*)[touches anyObject];
start = [touch locationInView:self.view].y;
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(start < 0)
{
return;
}
UITouch *touch = (UITouch *)[touches anyObject];
CGFloat now = [touch locationInView:self.view].y;
CGFloat diff = now - start;
sliderDirectionIsUp = diff < 0; //sliderDirectionIsUp is a BOOL
if ((self.userSlider.frame.origin.y == 0) && ((now < 120) || (now > 155))) {
return;
}
float newCenterY = self.userSlider.center.y + diff;
if (newCenterY < roundf(self.userSlider.frame.size.height / 2)) {
self.userSlider.center = CGPointMake(self.userSlider.center.x, newCenterY);
CGRect tableViewFrame = self.gameTableView.frame;
tableViewFrame.origin.y += diff;
tableViewFrame.size.height -= diff;
[self.gameTableView setFrame:tableViewFrame];
}
start = now;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect tableViewFrame = self.gameTableView.frame;
if (sliderDirectionIsUp)
{
tableViewFrame.origin.y = 28;
tableViewFrame.size.height = (self.view.frame.size.height - tableViewFrame.origin.y);
//animate userSlider out of visible area
[UIView animateWithDuration:.3 animations:^{
self.userSlider.center = CGPointMake(self.userSlider.center.x, -roundf(self.userSlider.bounds.size.height/2.) + 35);
[self.gameTableView setFrame:tableViewFrame];
}];
}
else if(start >= 0)
{
tableViewFrame.origin.y = (self.userSlider.frame.size.height - 7);
tableViewFrame.size.height = (self.view.frame.size.height - tableViewFrame.origin.y);
//animate userSlider with top to mainviews top
[UIView animateWithDuration:.3 animations:^{
self.userSlider.center = CGPointMake(self.userSlider.center.x, roundf(self.userSlider.bounds.size.height/2.) - 0.5);
[self.gameTableView setFrame:tableViewFrame];
}];
}
}
then both views would move how I wanted them to.
But now when I add it programmatically, suddenly the tableView doesn't change unless I tap inside the area after moving it. It's really weird.
Any ideas?

If your userSlider`s frame is
CGRectMake(0, -120, 320, 155)
I doubt whether your userSlider can receive the sender event .Because if a UIControl`s frame be out of the superView , it may not receive the sender event(the outside part) . You can set the frame
CGRectMake(0, 0, 320, 155)
to have a try .

Related

How to keep imageview inside UIView when moving imageview?

I have an UIView inside main view controller which contain 1 imageview.i moved imageview with this method.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
if (aTouch.view == self.sub_View) {
CGPoint location = [aTouch locationInView:self.sub_View];
CGPoint previousLocation = [aTouch previousLocationInView:self.sub_View];
self.imageView.frame = CGRectOffset(self.imageView.frame, (location.x - previousLocation.x), (location.y - previousLocation.y));
}
}
Imageview move perfect. But i have to keep imageview inside UIView when i moved. Problem with this code is when i move imageview it is also moved outside UIView. I have to keep imageview inside UIView and movable only inside UIView.
Please help me to solve this. How to set boundary for imageview so it is movable only inside UIView.
Try below code.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *aTouch = [touches anyObject];
if (aTouch.view == self.sub_View) {
CGPoint location = [aTouch locationInView:self.sub_View];
CGPoint previousLocation = [aTouch previousLocationInView:self.sub_View];
CGRect newFrame = CGRectOffset(self.imageView.frame, (location.x - previousLocation.x), (location.y - previousLocation.y));
if(newFrame.origin.x < 0)
newFrame.origin.x = 0;
if(newFrame.origin.y < 0)
newFrame.origin.y = 0;
if(newFrame.origin.x + newFrame.size.width > self.frame.size.width)
newFrame.origin.x = self.frame.size.width - self.imageView.frame.size.width;
if(newFrame.origin.y + newFrame.size.height > self.frame.size.height)
newFrame.origin.y = self.frame.size.height - self.imageView.frame.size.height;
self.imageView.frame = newFrame;
}
}
a) You can set the clipsToBounds property of your UIView to YES
b) You can use the fallowing code for moving your UIImageView
self.imageView.center = CGPointMake(MAX(0.0, MIN(location.x - previousLocation.x, self.sub_View.bounds.size.width)), MAX(0.0, MIN(location.y - previousLocation.y, self.sub_View.bounds.size.height)));

set up UITouch's moving range

I create a touch moving event for a label which size is (30, 50). here is the code
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
UIView *superView = self.superview;
}
But I need to make sure the label can only move inside the superview. In other word, UILabel will stop moving when it “touch” the edge of view, How to setup label's moving range?
Set the label to your touch position
Check whether the label's frame is inside of the superView
If it is outside,change the frame,and set userInteractionEnabled to NO
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.center = [touches.anyObject locationInView:self.superview];
CGRect bounds = self.superview.bounds;
CGRect newFrame = self.frame;
if (newFrame.origin.x < 0) {
newFrame.origin.x = 0;
self.userInteractionEnabled = NO;
}
if (newFrame.origin.y<0) {
newFrame.origin.y = 0;
self.userInteractionEnabled = NO;
}
if (newFrame.origin.x + newFrame.size.width > bounds.size.width) {
newFrame.origin.x = bounds.size.width - newFrame.size.width;
self.userInteractionEnabled = NO;
}
if (newFrame.origin.y + newFrame.size.height > bounds.size.height) {
newFrame.origin.y = bounds.size.height - newFrame.size.height;
self.userInteractionEnabled = NO;
}
self.frame = newFrame;
}

Problems making an Image Gallery using ImageView's and touchesBegan

I'm trying to create an image gallery for my iPhone app. I have laid out the imageViews and I want them to increase in size when the user clicks on them, and then return to normal when they release. The code I have made works for the first image, but the rest are totally unresponsive. Even if I remove the first one completely.
Here is the code I have been trying to use, does anyone have any ideas as to why it might not be working?
Thanks!
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == _imageOne)
{
CGRect newFrame = _imageOne.frame;
newFrame.size.width += 280;
newFrame.size.height += 280;
_imageOne.frame = newFrame;
}
if ([touch view] == _imageTwo)
{
CGRect newFrame2 = _imageTwo.frame;
newFrame2.size.width += 280;
newFrame2.size.height += 280;
_imageTwo.frame = newFrame2;
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == _imageOne)
{
CGRect newFrame = _imageOne.frame;
newFrame.size.width -= 280;
newFrame.size.height -= 280;
_imageOne.frame = newFrame;
}
if ([touch view] == _imageTwo)
{
CGRect newFrame2 = _imageTwo.frame;
newFrame2.size.width -= 280;
newFrame2.size.height -= 280;
_imageTwo.frame = newFrame2;
}
}

how to make a sliding up panel like the Google Maps app?

I'm looking for something like AndroidSlidingUpPanel for iOS. I found MBPullDownController, but it requires two ViewControllers to use, and requires a big change to the architecture of the app I'm working on to implement.
I just want something that adds a subview in an existing view controller. How would I go about doing that?
I use sliding up panels in my iOS apps quite a lot and I've found that the trick is to add a custom view to a view controller in the storyboard (or xib file) but to set its frame so that it is off the screen. You can ensure that the view stays off screen on any device using layout constraints.
Then it's just a case of animating the view on screen when appropriate. e.g.:
- (IBAction)showPanel:(id)sender
{
// panelShown is an iVar to track the panel state...
if (!panelShown) {
// myConstraint is an IBOutlet to the appropriate constraint...
// Use this method for iOS 8+ otherwise use a frame based animation...
myConstraint.constant -= customView.frame.size.height;
[UIView animateWithDuration:0.5 animations:^{
[self.view setNeedsLayout];
}];
}
else {
myConstraint.constant += customView.frame.size.height;
[UIView animateWithDuration:0.5 animations:^{
[self.view setNeedsLayout];
}];
}
}
If you want to have just a swipe up/down and that will reveal the panel you can use UISwipeGestureRecognizer like so:
- (void)viewDidLoad
{
[super viewDidLoad];
// iVar
swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipe:)];
swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
[self.view addGestureRecognizer:swipeUp];
// Do the same again with swipeDown using UISwipeGestureRecognizerDirectionDown...
}
- (void)didSwipe:(UIGestureRecognizer *)swipe
{
if (swipe == swipeUp) {
// Show panel (see above)...
} else {
// Hide panel (see above)...
}
}
If you want the panel to track your finger like when you open the Control Center, then you can use UIPanGestureRecognizer and get the translationInView: and velocityInView: and adjust the panel accordingly. Here is a snippet of code that tracks finger movement but using the touchesBegan:withEvent: - (void)touchesMoved:withEvent: and - (void)touchesEnded:withEvent: methods in a UIViewController to give you a taste:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Don't worry too much about buttonView this is another view that I animate upwards to get out the way of the panel as it slides in from the left...
[super touchesBegan:touches withEvent:event];
CGPoint loc = [[touches anyObject] locationInView:self.view];
// Save last touch for reference...
lastTouch = loc;
// leftBeginRect is an area where the user can start to drag the panel...
// trackFinger defines whether the panel should move with the users gestures or not...
if (CGRectContainsPoint(leftBeginRect, loc) && canTrack) {
trackFinger = YES;
}
// Left view is a reference to the panel...
else if (leftView.frame.size.width >= 300) {
// This means that the panel is shown and therefore should track the user's finger back towards the edge of the screen...
CGRect frame = CGRectMake(250, 0, 100, self.view.frame.size.height);
if (CGRectContainsPoint(frame, loc) && canTrack) {
trackFinger = YES;
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
CGPoint loc = [[touches anyObject] locationInView:self.view];
// Need to work out the direction in which the user is panning...
if (lastTouch.x > loc.x) {
currentFingerDirection = RHFingerDirectionLeft;
}
else {
currentFingerDirection = RHFingerDirectionRight;
}
lastTouch = loc;
if (trackFinger) {
if (loc.x <= 300) {
// This means that the panel is somewhere between fully exposed and closed...
// This is where the frame for the left view (and the constraints) are adjusted according to the user's current finger position...
CGRect frame = leftView.frame;
frame.size.width = loc.x;
[leftView setFrame:frame];
leftViewConstraint.constant = loc.x;
if (loc.x <= 80) {
float percentage = loc.x / 80;
int amount = 100 * percentage;
CGRect otherFrame = buttonView.frame;
otherFrame.origin.y = -amount;
[buttonView setFrame:otherFrame];
constraint.constant = constraintConstant + amount;
}
}
else {
CGRect frame = leftView.frame;
frame.size.width = 300;
[leftView setFrame:frame];
leftViewConstraint.constant = 300;
frame = buttonView.frame;
frame.origin.y = -100;
[buttonView setFrame:frame];
constraint.constant = constraintConstant + 100;
trackFinger = NO;
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// This method works out if the panel should pop open or spring closed when the user ends the gesture...
[super touchesEnded:touches withEvent:event];
if (trackFinger) {
CGPoint loc = [[touches anyObject] locationInView:self.view];
if (loc.x >= 50 && currentFingerDirection == RHFingerDirectionRight) {
CGRect frame = leftView.frame;
frame.size.width = 300;
leftViewConstraint.constant = 300;
CGRect otherFrame = buttonView.frame;
otherFrame.origin.y = -100;
constraint.constant = constraintConstant + 100;
[UIView animateWithDuration:0.2 animations:^{
[leftView setFrame:frame];
[buttonView setFrame:otherFrame];
}];
}
else if (loc.x <= 250 && currentFingerDirection == RHFingerDirectionLeft) {
CGRect frame = leftView.frame;
frame.size.width = 0;
leftViewConstraint.constant = 0;
CGRect otherFrame = buttonView.frame;
otherFrame.origin.y = 0;
constraint.constant = constraintConstant;
[UIView animateWithDuration:0.2 animations:^{
[leftView setFrame:frame];
[buttonView setFrame:otherFrame];
}];
}
else if (loc.x <= 150) {
CGRect frame = leftView.frame;
frame.size.width = 0;
leftViewConstraint.constant = 0;
CGRect otherFrame = buttonView.frame;
otherFrame.origin.y = 0;
constraint.constant = constraintConstant;
[UIView animateWithDuration:0.2 animations:^{
[leftView setFrame:frame];
[buttonView setFrame:otherFrame];
}];
}
else {
CGRect frame = leftView.frame;
frame.size.width = 300;
leftViewConstraint.constant = 300;
CGRect otherFrame = buttonView.frame;
otherFrame.origin.y = -100;
constraint.constant = constraintConstant + 100;
[UIView animateWithDuration:0.2 animations:^{
[leftView setFrame:frame];
[buttonView setFrame:otherFrame];
}];
}
trackFinger = NO;
}
currentFingerDirection = RHFingerDirectionNone;
}
The code is quite involved but it results in a nice panel animation that follows your finger like the Control Center.
Sorry for a late response. I created a small lib based on Rob-s answer.
https://github.com/hoomazoid/CTSlidingUpPanel
It's quite simple to use:
#IBOutlet weak var bottomView: UIView!
var bottomController:CTBottomSlideController?;
override func viewDidLoad() {
super.viewDidLoad()
//You can provide nil to tabController and navigationController
bottomController = CTBottomSlideController(parent: view, bottomView: bottomView,
tabController: self.tabBarController!,
navController: self.navigationController, visibleHeight: 64)
//0 is bottom and 1 is top. 0.5 would be center
bottomController?.setAnchorPoint(anchor: 0.7)
}

iOS - Collapsing Animated UISlider

I want to create a collapsing animated UISlider. Basicly, the slider will the size of the thumbimage until it is touched at which point it expands to full size while it is being changed. After the value is changed and the slider is let go, the slider will collapse back to the original size (the size of the thumbimage).
I tried working with touchesBegan and touchesEnd but this didn't get me very far.
So far i've subclassed UISlider and overrode the beginTrackingWithTouch and endTrackingWithTouch. This code kind of accomplishes the collapsing effect (Without the animation of course) but the thumb slider doesn't change anymore. Any ideas on how best to do this?
#import "CollapsingUISlider.h"
#implementation CollapsingUISlider
/*-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width * 4.0f, self.frame.size.height);
[super touchesBegan:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width * 0.25f, self.frame.size.height);
[super touchesEnded:touches withEvent:event];
}*/
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, 400, self.frame.size.height);
return YES;
}
-(void)endTrackingWithTouch:(UITouch*)touch withEvent:(UIEvent *)event {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, 20, self.frame.size.height);
}
-(BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, 400, self.frame.size.height);
return self.tracking;
}
#end
I ended up doing something like this:
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[platformView addGestureRecognizer:panRecognizer];
-(void)move:(id)sender {
[self.view bringSubviewToFront:[(UIPanGestureRecognizer*)sender view]];
CGPoint translatedPoint = [(UIPanGestureRecognizer*)sender translationInView:self.view];
if ([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {
firstX = [[sender view] center].x;
firstY = [[sender view] center].y;
}
translatedPoint = CGPointMake(firstX, firstY+translatedPoint.y);
[[sender view] setCenter:translatedPoint];
if (platformView.center.y < platformCenter.y - offset) {
platformView.center = CGPointMake(platformCenter.x,platformCenter.y-offset);
((UIPanGestureRecognizer*)sender).enabled = NO;
} else if (platformView.center.y > platformCenter.y) {
platformView.center = CGPointMake(platformCenter.x,platformCenter.y);
((UIPanGestureRecognizer*)sender).enabled = NO;
}
}

Resources