Please excuse poor code atm, just trying to get it working before making it look nice. So I am trying to get UIScrollView working. If I remove all scroll view stuff, I have a nicely laid out page with 64 buttons, 32 on each side of the page. These buttons are nigh miniature; so I wanted to implement zoom to be able to click them.
Zoom currently has unexpected results. When the page starts, it is blank. Zooming unexpectedly shows some of the left side of the graph on the rigght side of the page, and it bounces as I try to scroll over towards it. But when I zoom more, it allows me to scroll more towards the middle of the buttons. Always giving me difficulties/bugging out as I scroll/zoom. So obviously unusable.
My viewDidLoad:
[super viewDidLoad];
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scroll.minimumZoomScale = 0.5;
scroll.maximumZoomScale = 3.0;
scroll.delegate = self;
CGFloat yOrigin = self.view.frame.size.width;
UIView *awesomeView = [[UIView alloc] initWithFrame:CGRectMake(yOrigin, 0, self.view.frame.size.width, self.view.frame.size.height)];
// iterate over values in the staff array
int heightBetweenBrackets = 0;
int widthBetweenBrackets = 0;
int heightFromTop = 45;
for(int i = 0; i < 64; i++)
{
if(i == 32)
{
heightBetweenBrackets = 0;
}
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
myButton.frame = CGRectMake(
0 + i/32*438,
heightFromTop + i%32*3+ heightBetweenBrackets,
35, 6);
[myButton setTitle:[NSString stringWithFormat:#"%d",i] forState:UIControlStateNormal];
myButton.titleLabel.adjustsFontSizeToFitWidth = YES;
[myButton.titleLabel setFont:[UIFont fontWithName:#"Arial" size:7]];
myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
// [myButton addTarget:self action:#selector(chooseWinner:) forControlEvents:UIControlEventTouchUpInside];
[awesomeView addSubview:myButton];
heightBetweenBrackets += (i%2 -1 * -1) * 3;
}
[scroll addSubview:awesomeView];
scroll.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
[self.view addSubview:scroll];
and:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.view;
}
I apologize if the bug is stupid I'm just learning IOS =] thanks for your time
EDIT: Figured it out.
For anybody in the future: I ported over a pagination scroller, and didn't realize I had kept CGFloat yOrigin = self.view.frame.size.width; -- this of course was starting the view directly to the right of any visible space. Thus I was able to zoom and see the left of it, in a buggy manner, although it started out blank. Simply changing this to 0 solved my problem.
For anybody in the future: I ported over a pagination scroller, and didn't realize I had kept CGFloat yOrigin = self.view.frame.size.width; -- this of course was starting the view directly to the right of any visible space. Thus I was able to zoom and see the left of it, in a buggy manner, although it started out blank. Simply changing this to 0 solved my problem.
Related
I would like to generate a shockwave effect when I tap on a circular UIView. The effect I'm looking for is very similar to 'Circadia' on the App Store.
(source: mzstatic.com)
Note the circular line that is expanding from the centre view. I couldn't find any kind of tutorial, so I tried the effect using a cheat:
-(void)startShockwave:(UIView *)target
{
// Replace the view tapped with one that will expand
UIView *wave = [[UIView alloc]initWithFrame:target.frame];
wave.backgroundColor = target.backgroundColor;
wave.alpha = 0.5;
wave.layer.masksToBounds = YES;
wave.layer.cornerRadius = wave.frame.size.width / 2;
// Hide it below the original view
[self.view insertSubview:wave belowSubview:target];
CGRect frame = wave.frame;
// Create a view that is the same colour as self.view to make it look like a hole
UIView *center = [[UIView alloc]initWithFrame:CGRectMake(frame.origin.x + 10, frame.origin.y + 10, frame.size.width - 20, frame.size.height - 20)];
center.backgroundColor = self.view.backgroundColor;
center.layer.masksToBounds = YES;
center.layer.cornerRadius = center.frame.size.width / 2;
[self.view insertSubview:center aboveSubview:wave];
// Hide the original view
target.alpha = 0;
// IMPORTANT: I send these views behind the others so that center does not overlap them
[self.view sendSubviewToBack:center];
[self.view sendSubviewToBack:wave];
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
CGAffineTransform waveTransform = wave.transform;
CGAffineTransform centerTransform = center.transform;
// Expand the views to look like a shockwave
wave.transform = CGAffineTransformScale(waveTransform, 4, 4);
center.transform = CGAffineTransformScale(centerTransform, 5.75, 5.75);
// Fade the wave out to nothing
wave.alpha = 0;
} completion:^(BOOL finished) {
// Remove the shockwave
[wave removeFromSuperview];
[center removeFromSuperview];
}];
}
This works quite well...
...With only one shockwave. However, when they intersect, one center overlaps the other due to the most recent shockwave being sent to the back.
I would prefer intersecting shockwaves to be more like this:
I'm not sure how to create this type of effect, however, so any help is much appreciated!
Use [UIColor clearColor] to make the center of your circle transparent (so you can see interface elements residing beneath it).
center.backgroundColor = [UIColor clearColor];
It's a quick fix! You already did most of it. Just change this line:
center.backgroundColor = self.view.backgroundColor;
to
center.backgroundColor = [UIColor clearColor];
I'm creating an app that can slide between pages of a book, left and right (covering or revealing like a card deck), and sliding up and down will either slide down (and cover) a settings page, or slide up (and reveal) to detailed info on the book.
Essentially, its a "card deck" Sliding functionality, similar to this project https://github.com/sweetmandm/CardSliderView-for-iOS which I'm considering using, however I require BOTH vertical AND horizontal "card deck" sliding capability.
To give you another example of the slide/left cover/reveal effect I'm looking, take a look at the new CNN app, where you can slide between articles.
I've also considered using UIPageViewController, however this does not support the "Slide and Reveal / Slide and Cover" transition I'm looking for, rather only a "Slide Over Left or Right" transition, so I would have to hack it somehow and use multiple UIPageViewControllers, one on top of the over, to allow the "reveal and cover" effect to work, using just the gestures from the UIPageViewController to allow the user to swipe.
I'm familiar with the directionalLockEnabled property on UIScrollview, however I'm wondering still what is the overall best approach to get the effect I'm looking for, one that will support both vertical and horizontal, UIScrollView? UIPageViewController? Nested UIScrollviews? Instead of playing around with the directionalLockEnabled property? Something Else?
What would be the best way to achieve the exact user experience I'm looking to provide?
Ok, I found a solution to my own question, using nested UIScrollViews. I also added a very rough solution as project to github: https://github.com/cohen72/InfiniteDeckScroller.
For the horizontal scrolling, I have three horizontal scroll views, one nested into the other. UIScrollview is automatically handling the proper scrolling of each. Depending on the content offset and which scrollview is being scrolled, I know how to "re-arrange/re-order" the nested scrollviews.
Here is a snippet of the solution I came up.
This particular solution allows sliding up to reveal, however I did not yet implement the sliding down to cover, however doing so would use the same approach as the horizontal solution.
#define BOTTOM 1
#define MIDDLE 2
#define TOP 3
#define VERTICAL_SUB 4
- (void)viewDidLoad{
[super viewDidLoad];
UIScrollView *scroll1 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
UIScrollView *scroll2 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
UIScrollView *scroll3 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
UIScrollView *scroll4 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scroll1.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height * 1);
scroll1.tag = BOTTOM;
scroll1.pagingEnabled = YES;
scroll1.bounces = NO;
scroll1.delegate = self;
[scroll1 addSubview:[self labelForScrollView:scroll1 withBgColor:[UIColor redColor]]];
[scroll1 setContentOffset:CGPointMake(0, 0)];
scroll2.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height * 1);
scroll2.tag = MIDDLE;
scroll2.pagingEnabled = YES;
scroll2.bounces = NO;
scroll2.delegate = self;
[scroll2 addSubview:[self labelForScrollView:scroll2 withBgColor:[UIColor orangeColor]]];
[scroll2 setContentOffset:CGPointMake(0, 0)];
scroll3.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height * 1);
scroll3.tag = TOP;
scroll3.pagingEnabled = YES;
scroll3.bounces = NO;
scroll3.delegate = self;
[scroll3 addSubview:[self labelForScrollView:scroll3 withBgColor:[UIColor yellowColor]]];
[scroll3 setContentOffset:CGPointMake(320, 0)];
scroll4.contentSize = CGSizeMake(self.view.frame.size.width * 1, self.view.frame.size.height * 2);
scroll4.delegate = self;
scroll4.bounces = NO;
scroll4.pagingEnabled = YES;
scroll4.alwaysBounceVertical = NO;
scroll4.tag = VERTICAL_SUB;
[scroll4 addSubview:scroll1];
[scroll1 addSubview:scroll2];
[scroll2 addSubview:scroll3];
[self.view addSubview:scroll4];
}
- (UILabel*)labelForScrollView:(UIScrollView*)scrollView withBgColor:(UIColor*)color{
UILabel *lbl = [[UILabel alloc] initWithFrame:scrollView.bounds];
lbl.textAlignment = NSTextAlignmentCenter;
lbl.backgroundColor = color;
lbl.text = [NSString stringWithFormat:#"ScrollView: %d", scrollView.tag];
return lbl;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
NSLog(#"content offset: %f, tag: %d ", scrollView.contentOffset.x, scrollView.tag);
UIScrollView *newMiddleScrollView, *newBottomScrollView, *newTopScrollView;
// swipe left
if (scrollView.contentOffset.x == 0 && scrollView.tag == TOP) {
newMiddleScrollView = (UIScrollView*)[self.view viewWithTag:TOP];
newTopScrollView = (UIScrollView*)[self.view viewWithTag:BOTTOM];
newBottomScrollView = (UIScrollView*)[self.view viewWithTag:MIDDLE];
}
// swipe right
else if (scrollView.contentOffset.x == 320 && scrollView.tag == MIDDLE) {
newMiddleScrollView = (UIScrollView*)[self.view viewWithTag:BOTTOM];
newTopScrollView = (UIScrollView*)[self.view viewWithTag:MIDDLE];
newBottomScrollView = (UIScrollView*)[self.view viewWithTag:TOP];
}
else {
return;
}
newMiddleScrollView.tag = MIDDLE;
newBottomScrollView.tag = BOTTOM;
newTopScrollView.tag = TOP;
newBottomScrollView.contentOffset = CGPointMake(0, 0);
newMiddleScrollView.contentOffset = CGPointMake(0, 0);
newTopScrollView.contentOffset = CGPointMake(320, 0);
UIScrollView *verticalScrollView_sub = (UIScrollView*)[self.view viewWithTag:VERTICAL_SUB];
[verticalScrollView_sub addSubview:newBottomScrollView];
[newBottomScrollView addSubview:newMiddleScrollView];
[newMiddleScrollView addSubview:newTopScrollView];
}
That's a good question - correct me if I'm wrong here, but it sounds that from left to right there's an arbitrary number of pages/cards, but only a few standard cards to come in from the top or bottom (your settings and details panels).
If that's the case, then you may well want to stick with something along the lines of a UIPageController alongside some gesture recognisers. You set up your page controller (or whatever controller you end up using to achieve your desired cards animation), and then add two gesture recognizers for swipe up and swipe down.
You can then animate your details/settings views in when you receive those gestures, giving you the card like interface without needing to bother with multiple UIPageViewControllers or a scroll view.
This approach isn't so great if you want an arbitrary number of cards in both the horizontal and vertical, but it sounds as if that's not the case.
There would be a lot of different ways to accomplish this, depending on how you want your different controllers to relate to each other. The slide transition itself is pretty simple. I've implemented it this way in a controller that's the superclass of the controller that calls the method:
-(void)SlideInController:(RDSlideController *) next {
next.presentingVC = self;
next.view.frame = CGRectMake(self.view.frame.origin.x + 320, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
[self.view.window addSubview:next.view];
[UIView animateWithDuration:1 animations:^{
next.view.frame = self.view.frame;
} completion:^(BOOL finished) {
self.view.window.rootViewController = next;
}];
}
Then from the newly presented controller, you can call this method to go back:
-(void)SlideOut {
UIViewController *prev = self.presentingVC;
prev.view.frame = self.view.frame;
[self.view.window insertSubview:prev.view belowSubview:self.view];
[UIView animateWithDuration:1 animations:^{
self.view.frame = CGRectMake(self.view.frame.origin.x + 320, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
} completion:^(BOOL finished) {
self.view.window.rootViewController = prev;
}];
}
the problem I've met today is with my subclass of UIPageControl. When I initialize it, the frame (specifically the origin) and image of dots stays default, which is the problem, since I want it to change right after initialization. However, when I move with scrollView (as in "touch and move") after initialization, they (the dots) somehow jump to the right position with correct images.
What could be the problem?
Code:
CustomPageControl.m
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
activeImage = [UIImage imageNamed:#"doton.png"];
inactiveImage = [UIImage imageNamed:#"dotoff.png"];
return self;
}
- (void) updateDots
{
for (int i = 0; i < [self.subviews count]; i++)
{
UIImageView *dot = [self.subviews objectAtIndex:i];
if (i == self.currentPage) dot.image = activeImage;
else dot.image = inactiveImage;
[dot setFrame:CGRectMake(i * 13.5, 1.5, 17, 17)];
}
}
- (void)setCurrentPage:(NSInteger)currentPage
{
[super setCurrentPage:currentPage];
[self updateDots];
}
#end
ChoosingView.m - init part
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 160, 300)];
[scrollView setBackgroundColor:[UIColor clearColor]];
[scrollView setDelaysContentTouches:NO];
[scrollView setCanCancelContentTouches:YES];
[scrollView setClipsToBounds:NO];
[scrollView setScrollEnabled:YES];
[scrollView setPagingEnabled:YES];
[scrollView setShowsHorizontalScrollIndicator:NO];
[scrollView setShowsVerticalScrollIndicator:NO];
pageControl = [[CustomPageControl alloc] initWithFrame:CGRectMake(200, 300, 80, 20)];
[pageControl setBackgroundColor:[UIColor clearColor]];
pageControl.numberOfPages = 6;
[pageControl setCurrentPage:0];
the last line is when I would expect the UIPageControl to refresh, however that does not happen.
Does this happen with the standard UIPageControl implementation?
Your problem states that your objects subViews (eg the UIImageViews) rects/size are not initialising to your desired size/position.
I implemented this code in my project with a nib rather than programmatically and I needed to call -(void)updateDots to set it as its initial condition was the standard dots..
I dont see how the UIScrollView has any bearing impact on this unless somehow its linked to your -(void)updateDots function (E.g. your setting the currentIndex of your custom page control). You state, "However, when I move with scrollView (as in "touch and move") after initialization, they (the dots) somehow jump to the right position with correct images."
Because they "jump to the right position with correct images" it means that your -(void)updateDots function must be getting called. I dont see any other explanation.
Also your iteration loop assumes that all the UIViews in your .subViews array are UIImageViews, although fairly safe to assume this, I would check to see if the UIView is a UIImageView with reflection.
I'm trying to make code that will add players to a view based on objects but, I'm having some issues.
Right now currently if I run the initView method with 4 confirmed working Player objects in playerList, only 3 UIViews will appear in my scrollview, then when I click the clear players button, only the last (3rd) UIView will be removed.
A side note, none of my custom buttons appear in the UIViews either, I have an image that they should load with, but its not working.
Thanks in advance for any help.
- (void)clearPlayers {
for (Player* i in self.playerList) {
[i.viewPane removeFromSuperview];
}
[self.playerList removeAllObjects];
}
- (void)initView {
int Loc = 0;
int Count = 1;
int margin = 5;
int height = 100;
for (Player *p in playerList) {
UIView *playerView = [[UIView alloc] initWithFrame:CGRectMake(0, Loc, 320.0, height)];
p.viewPane = playerView;
[playerView setBackgroundColor:[UIColor redColor]];
[scrollView addSubview:playerView];
UIButton *plus = [[UIButton alloc] initWithFrame:CGRectMake(200, 10, (height - 5), (height - 5))];
UIImage *buttonImage =[[UIImage alloc] initWithContentsOfFile:#"Metal_Plus_Up_2.png"];
[plus setBackgroundImage:buttonImage forState:UIControlStateNormal];
[p.viewPane addSubview:plus];
[plus release];
[playerView release];
Loc = Loc + (height + margin);
Count = Count + 1;
}
[scrollView setContentSize:CGSizeMake(320.0, (height * Count) + (margin * Count))];
}
Question: have you verified that the UIImage returned from initWithContentsOfFile is not nil? You might need the full path instead of just the filename
As far as the wackiness with the UIViews not getting removed goes, everything you've posted looks fine as far as I can see. The only thing I can think of is that maybe you don't have retain specified as an attribute for your viewPane property...
I have a really weird problem with UIImageView. I have an image (an RGB png) 45x45 pixels which I add to the view. I can see that image is blurred after added to the view. Here is the same image in the simulator (left) and in Xcode (right):
(source: partywithvika.com)
(source: partywithvika.com)
I have custom UIImageView class with this initWithImage code:
- (id) initWithImage:(UIImage*) image {
self = [super initWithImage:image];
self.frame = CGRectMake(0, 0, 45, 45);
self.contentMode = UIViewContentModeScaleAspectFit;
self.quantity = 1;
if (self) {
self.label = [[UITextField alloc]initWithFrame:CGRectMake(0,40,45,25)];
self.label.font = [UIFont systemFontOfSize:16];
self.label.borderStyle = UITextBorderStyleNone;
self.label.enabled = TRUE;
self.label.userInteractionEnabled = TRUE;
self.label.delegate = self;
self.label.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
self.label.textAlignment = UITextAlignmentCenter;
}
self.userInteractionEnabled = TRUE;
// Prepare 3 buttons: count up, count down, and delete
self.deleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.deleteButton.hidden = NO;
self.deleteButton.userInteractionEnabled = YES;
self.deleteButton.titleLabel.font = [UIFont systemFontOfSize:20];
self.deleteButton.titleLabel.textColor = [UIColor redColor];
[self.deleteButton setTitle:#"X" forState:UIControlStateNormal];
[self.deleteButton addTarget:self action:#selector(deleteIcon:) forControlEvents:UIControlEventTouchUpInside];
self.upCountButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.upCountButton.hidden = NO;
self.upCountButton.userInteractionEnabled = YES;
[self.upCountButton setTitle:#"+" forState:UIControlStateNormal];
[self.upCountButton addTarget:self action:#selector(addQuantity:) forControlEvents:UIControlEventTouchUpInside];
self.downCountButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.downCountButton.hidden = YES;
self.downCountButton.userInteractionEnabled = YES;
[self.downCountButton setTitle:#"-" forState:UIControlStateNormal];
[self.downCountButton addTarget:self action:#selector(removeQuantity:) forControlEvents:UIControlEventTouchUpInside];
return self;
}
I create it like this:
UIImage *desertIcon = [UIImage imageNamed:#"desert.png"];
IconObj *desertIconView = [[IconObj alloc] initWithImage:desertIcon];
desertIconView.center = CGPointMake(265,VERTICAL_POINT_ICON);
desertIconView.type = [IconObj TYPE_DESERT];
[self.view addSubview:desertIconView];
[desertIconView release];
Why would the displayed image be so than the one stored in a file?
In general, everything you do on iPhone with UIKit should be pixel-aligned. Problems with pixel alignment usually manifest as blurriness (this is especially true with text and images). This is why when I find blurry view, I first check if I'm setting the center property. When you set center, the frame's x, y, height and width are adjusted around that center point... frequently resulting in fractional values.
You could try using CGRectIntegral on the frame as shown:
desertIconView.center = CGPointMake(265,VERTICAL_POINT_ICON);
desertIconView.frame = CGRectIntegral(desertIconView.frame);
This may work, but if it doesn't, try setting the frame manually, without using center property to ensure that no values are fractional.
Edit: Whoops! Didn't notice that the OP had answered his own question... I'll leave this up for informational reasons anyway.
I had this problem and it was driving me nuts. After some investigation it turned out that my image was smaller than the frame, and hence the scaling up blurred it. Once I had put in higher resolution images the problem is gone.
Make sure your image size is greater than your UIImageView frame size.
Your IconObj is 45 pixels wide. You move your IconObj center to 265 which makes its frame to (242.5, VERTICAL_POINT_ICON - 25*0.5, 45, 25). Image will always be blur if some of frame parameter is not integer.
Solution, calculate the frame parameter yourself (don't use center). And always make it integer (cast to NSInteger, use ceil, floor, or round).
desertIconView.frame = CGRectMake((NSInteger)(265 - 45*0.5),
(NSInteger)(VERTICAL_POINT_ICON - 25*0.5),
45, 25);
What I ended up doing is loading bigger picture into 45x45 UIImageView, of course with
contentMode = UIViewContentModeScaleAspectFit;
I just had the same problem. First I thought wtf do I need glasses?
Then I realized it´s just not possible to center an image (or label) when you give it an odd number. You need to resize your image to e.g. 46*46 if you want to center it and stay sharp.
Or you can leave it at 45*45 but then you need to decide whether you want your image to be mal-centered 1 pixel to the right, or 1 pixel to the left. (or leave it blurry).
Try to align your IconObj view at screen pixels. If it's really 45x45 then you should set the center to something like CGPointMake(x + 0.5f, y + 0.5f).
You should also double check image size in pixels (e.g in Preview.app Command-I).
complete source for the offset problem:
contentImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
UIImage *cImage = [UIImage imageNamed:image];
self.contentImage.image = cImage;
[self.contentImage setFrame:CGRectMake(0, 0, cImage.size.width, cImage.size.height)];
CGFloat offset = 0.0f;
if ( ( (int)cImage.size.width % 2 ) && ( (int)cImage.size.height % 2 ) ) {
offset = 0.5f;
}
[self.contentImage setCenter:CGPointMake(256.0f + offset, 256.0f + offset)];