Confused with a simple example of paging and panning. Obj-C - ios

Studying iOs Development by - The Big Nerd Ranch Guide" (Conway and Hillegass)
Chapter "Subclassing UIView and UIScrollView"; Panning and paging.
The following chunk of code being typed in the
- (BOOL)application: didFinishLaunchingWithOptions: method.
(HypnosisView - is a custom made class that actually performs the drawing on the screen.)
Can't understand following code:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//-------Adding a scrool option-----------
CGRect screenRect=[[self window] bounds];
// create the UIScrollView to have the size of the window, matching its size
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect];
[scrollView setPagingEnabled:YES];
[[self window] addSubview:scrollView];
// create the HypnosisView with a frame that is twice the size of the screen (with a big
// width)
CGRect bigRect = screenRect;
bigRect.size.width *=2.0;
HypnosisView *view=[[HypnosisView alloc] initWithFrame:screenRect];
// add the HypnosisView as a subview of the scrollView istead of the window
[scrollView addSubview:view];
// move the ractangle for the other HypnosisView to the right, just off the screen
screenRect.origin.x = screenRect.size.width;
HypnosisView *anotherView = [[HypnosisView alloc] initWithFrame:screenRect];
[scrollView addSubview:anotherView];
// tell the scrollView how big its virtual world is
[scrollView setContentSize:bigRect.size];
So our goal is to create a view instance with an width bigger than the iphone screen.
First we are declaring new variable "screenRect" that has bounds of a "window".
Then we are creating an instance of "UIScrollView" that has frame dimensions same as the
"screenRect" same as the window.
Making paging enabled.
Adding our newly created "scrollView" to the hierarchy of views.
So we have parent "window" and child "scrollView" (that has same dimensions as our main window)
Declaring a new variable "bigRect", and making it equal to our previously declared "screenRect".
Setting bigRect's "width" property to be twice as much.
Creating a new "view" object that is an instance of our custom made Hypnosis class that actually performs the drawing. We set our view's frame to be the same as our "screenRect" frame.
Adding our newly created "view" to the hierarchy of views. Now we have 3 level hierarchy: UIWindow--> UIScrollView-->HypnosysView
9.Now here, I don't understand what this line of code does and why do we need it (screenRect.origin.x = screenRect.size.width;)
10). Why are we creating another instance of HypnosisView in the next line?
11). at the end we notify scrollView about how big its size.

9.Now here, I don't understand what this line of code does and why do we need it (screenRect.origin.x = screenRect.size.width;)
10). Why are we creating another instance of HypnosisView in the next line?
The example will display 2 HypnosisViews which are side by side in a scroll view. The second one is off screen. So you have to drag/page the scroll view to see it.
screenRect.origin.x = screenRect.size.width
This just positions the 2nd hypnosis view to the right of the fist one.

Related

Put one SubView more than once on a UIScrollView

I have an UIScrollView with UIViews being added by lazy-loading algorithm. The UIViews contain previews of previous and next image, so at some point the actual views have to be used more than once (e.g. preview of the 2nd picture is NEXT now, but after 2 page scrolls it is PREVIOUS).
All the UIViews are stored in NSMutableArray and are added dynamically. I have the code, which checks, whether particular UIView is already added to the UIScrollView. If not, the code checks, is there needed UIView in NSMutableArray and if not, creates a new one...
The problem occurs when I try to get an UIView from NSMutableArray, change it's position on the screen and add to UIScrollView again. It just changes it's position and I see white squares where some UIViews have to be.
Is it possible to have 2 UIViews with pointer referred to one memory location or UIImage but with only one different parameter (position)? Is it good idea to copy already created UIView to NSMutable array twice to use with different position? (Win: creation process takes place only once, loss: amount of memory required is doubled).
From my point of view, I havent fount the good solution, but this worked for me - I just implemented copy method in UIView subclass, something like this:
- (ExhibitPreview *)copy {
ExhibitPreview *exhibitPreview =[[ExhibitPreview alloc] initWithImage:self.image];
exhibitPreview.number.text = self.number.text;
exhibitPreview.author.text = self.author.text;
exhibitPreview.title.text = self.title.text;
exhibitPreview.contentMode = UIViewContentModeScaleAspectFill;
exhibitPreview.clipsToBounds = YES;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGRect frame = CGRectMake(0.0f, 0.0f, screenWidth/2, PREVIEW_HEIGHT);
exhibitPreview.frame = frame;
return exhibitPreview;
}
The first view is view itself and the second I create is a copy.

Shift frame inside UIPopoverController

My universal app uses NIBs for its settings screens. I'd like to use the same NIBs for both iPhone and iPad.
Thus on iPad, I use a UIPopoverController in the MainViewController and for settings, simply display the iPhone-sized NIBs, to show what is called the SettingsViewController. The popover is sized 320x460 points.
This causes a problem, because the iPhone version draws a number of things above the status bar programmatically, and for the iPad version this is not necessary. Current situation on iPad:
As you can see, there's a big empty space above the "Settings" title. Thus what I want, is to shift the view controller up about 20 points, inside the popover:
The popover is instantiated as follows in the MainViewController:
settingsPopoverController = [[UIPopoverController alloc] initWithContentViewController:popoverNavigationController];
settingsPopoverController.popoverContentSize = CGSizeMake(320, 460);
settingsPopoverController.delegate = self;
popoverNavigationController.navigationBarHidden = YES;
In the SettingsViewController, I set the frame as follows:
- (void)viewDidLoad
{
self.contentSizeForViewInPopover = CGSizeMake(320, 460);
}
And later in the SettingsViewController, I try to create an offset as follows:
- (void)viewWillLayoutSubviews
{
// shift it up
CGRect frame = self.view.frame;
frame.origin.y = -20;
[self.view setFrame:frame];
}
This does not shift the content up a bit. How to go about?
To clarify: I want to move down the "viewport" that the popover shows.
Try to:
myPopover.autoresizingMask=UIViewAutoresizingNone;
Or:
myPopover.autoresizingMask=UIViewAutoresizingFlexibleHeight || UIViewAutoresizingFlexibleWidth;
You can also try to put your code in -(void)viewDidLayoutSubviews method.
If this answers did not help, try set popoverLayoutMargins (property) instead of setFrame: for example:
popover.popoverLayoutMargins=UIEdgeInsetsMake (
CGFloat 50, //top
CGFloat 50,//left
CGFloat 50,//bottom
CGFloat 50//right
);

UIScrollView - content goes out bound when scrolling

I design the scrollview in interface builder like this
It looks good here. But unfortunately when I run it on emulator or device
it becomes
The content in scrollview is expand outside scrollview itself and even though outside UIView that contains this scrollView.
In my viewDidLoad (panel is the container of scrollView )
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
CGFloat adjustPanelHeight = [PTTScreenScaleUtil getAdjustHeight:self.panel.frame.size.height];
CGRect panelRect = self.panel.frame;
panelRect.size.height = adjustPanelHeight;
self.panel.frame = panelRect;
UIImage *image = [UIImage imageNamed:#"panel-background"];
UIGraphicsBeginImageContext(self.panel.frame.size);
[image drawInRect:CGRectMake(0, 0, self.panel.frame.size.width, adjustPanelHeight)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.panel setBackgroundColor:[UIColor colorWithPatternImage:newImage]];
NSLog(#"scrollView Height : %f", self.scrollView.frame.size.height);
NSLog(#"scrollView contentSize Height : %f", self.scrollView.contentSize.height);
// CGRect scrollViewRect = self.scrollView.frame;
// CGRect scrollViewContentRect = self.scrollView.frame;
// NSLog(#"ScrollView Height Before : %f , After : %f", self.scrollView.frame.size.height, [PTTScreenScaleUtil getAdjustHeight:self.scrollView.frame.size.height]);
// scrollViewRect.size.width = 280;
// scrollViewRect.size.height = [PTTScreenScaleUtil getAdjustHeight:270];
// self.scrollView.frame = scrollViewRect;
// [self.detailsLabel sizeToFit];
UIView *view = [[self.scrollView subviews] objectAtIndex:0];
// [view sizeToFit];
// [self.scrollView setContentMode:UIViewContentModeScaleAspectFit];
// NSLog(#"ContentSize Height : %f", view.frame.size.height);
// scrollViewContentRect.size.height = view.frame.size.height;
NSLog(#"Bounds : %f", view.bounds.size.height);
self.scrollView.frame = CGRectMake(10, 10, 280, 270);
self.scrollView.contentSize = CGSizeMake(280, 500);
NSLog(#"Frame Height %f", self.scrollView.frame.size.height);
//[self.scrollView setContentSize: CGSizeMake(280, 1000)];
CGRect termBtnRect = self.termBtn.frame;
CGRect mailBtnRect = self.mailBtn.frame;
CGRect twitterBtnRect = self.twitterBtn.frame;
CGRect fbBtnRect = self.fbBtn.frame;
termBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
mailBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
twitterBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
fbBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
self.termBtn.frame = termBtnRect;
self.mailBtn.frame = mailBtnRect;
self.twitterBtn.frame = twitterBtnRect;
self.fbBtn.frame = fbBtnRect;
}
All the log return 270.0
PS. the scroll bar is correct even though the content goes outside but the scroll bar is working correctly (stay in the scrollview's frame as arrange in interface builder)
I have no idea how can I solve this.
Anyone help me please.
Thanks you.
Solve it by creating new view controller in interface builder and redo the same process with careful and bingo. It works.
When I compare both two view controller I realise that the wrong one UIScrollView Clip Subviews is unchecked. When check it the problem solve.
I just struggled with this for an hour and had a head smack moment.
In my case, I had a UIView on the scene in the Storyboard. At some point I decided I needed it to be a UIScrollView instead (as opposed to the original plan which was to embed the UIScrollView in a UIView)
I went ahead and changed the class on the UIView to UIScrollView. IB changed it to Scroll View in the Document Outline, I figure I'm good, right?
And then I see the behavior you describe.
At some point it hits me that this isn't sufficient. Apparently adding a UIScrollView via IB does some things differently than adding a UIView and just changing class isn't enough. And this is probably the reason re-doing it from scratch fixed it for you.
So for anyone who runs into this in the future, make sure you added the UIScrollView via IB instead of a UIView
I was having the same problem as described in this post. I tried multiple combinations of solutions that did not work, including:
putting the scroll view inside a view with Clip To Bounds = YES
putting a view inside the scroll view with Clip To Bounds = YES, that then contained my child view
putting a Container View inside the scroll view, and then embedding my subview
rebuilding the Interface Builder files completely
every combination of autosizing mask options systematcially for both the scroll view and container view
clip to bounds enabled or disabled for every single element systematically
The child view in question had previously worked inside a scroll view, but wouldn't in this one case where the content blew outside the bounds of the scroll view.
In the end, I implemented the solution in code as I could find no way to get Interface Builder to co-operate:
// There are two scroll areas on the screen, the left view and the right view.
// We want the right view to contain a scrollable area with another child view controller
// we designed in Interface Builder.
// create a scroll view to fill the right view with a scrollable area
CGSize rightFrameSize = self.rightView.bounds.size;
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake (0, 0, rightFrameSize.width, rightFrameSize.height)];
scrollView.contentSize = CGSizeMake(640, 1352);
[self.rightView addSubview:scrollView];
// now create our child view controller from Interface Builder and add it to the scroll view
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"CustomerAddress" bundle:[NSBundle mainBundle]];
detailsView = [sb instantiateViewControllerWithIdentifier:#"CustomerDetailsView"];
detailsView.delegate = self;
detailsView.customer = _customer;
[scrollView addSubview:detailsView.view];
You could of course get the scrollView.contentSize from the child view controller you constructed in Interface Builder using scrollView.contentSize = detailsView.view.frame.size.
I have a love/hate relationship with Interface Builder... most days I love it, but some days we argue and I wish we'd never met... :)
only make cliptobound=YES in storyboard if you changed UIView to UIScrollView

Setting the accessibilityFrame of an element whose parent view will move

I have a UITableView with a custom header (i.e. I create the UIView myself). I need to tweak the accessibilityFrame of one of the subviews of the view, but I can’t figure out how to set the coordinates of the frame appropriately—they need to be relative to the window, but I’m not sure how to accomplish that.
My code looks like
- (UIView *)tableView:(UITableView *)tableView
viewForHeaderInSection:(NSInteger) section
{
CGRect bounds = CGRectMake(0, 0, [tableView frame].size.width, 48);
UIView *header = [[UIView alloc] initWithFrame:bounds];
UILabel *labelOne = [[UILabel alloc] initWithFrame:
CGRectMake(0, 0, bounds.size.width - 80, 18)];
UILabel *labelTwo = [[UILabel alloc] initWithFrame:
CGRectMake(0, 20, bounds.size.width - 80, 18)];
CGRect frameOne = [labelOne frame];
CGRect frameTwo = [labelTwo frame];
[labelTwo setIsAccessibilityElement:NO];
[labelOne setAccessibilityFrame:CGRectUnion(frameOne, frameTwo)];
// ...
return header;
}
I’ve got two UILabels, which I want to combine into one for the purposes of VoiceOver. I accomplish this by ignoring the second label and extending the frame of the first label to cover the area of the second label. (The second label is immediately below the first.) The problem is getting the frames. If I use the code as shown above, the accessibility frame is the correct size, but is positioned as if the UITableView’s header were in the top left corner of the screen. I tried to modify the code to say
CGRect frameOne = [header convertRect:[labelOne frame] toView:nil];
CGRect frameTwo = [header convertRect:[labelTwo frame] toView:nil];
but the same thing happened. Shouldn’t this latter piece of code convert the UILabels’ frames into window-relative coordinates?
I thought maybe the issue is that when the UIView is created, it doesn’t know where on screen it’s going to be positioned (and as part of a UITableView it may be scrolled all over the place). Is it necessary to implement accessibilityFrame as a message which checks the UIView’s position each time it is called?
There's a helper function that will assist you with doing exactly that: UIAccessibilityConvertFrameToScreenCoordinates. This function takes a CGRect and converts it from a view's coordinate system into screen coordinates.
I don't think it's the timing of when the UIView is created, as I believe the window should be not-nil by the time tableView:viewForHeaderInSection: is called. I think the problem is the receiver of the convertRect:toView: message. Rather than passing this message to header, you should be passing it to [self view].
You're converting from the receivers coordinate system to that of another view, in this case nil or the UIWindow in your app. When header receives this message, you're converting from header's coordinate system to window's coordinate system, but header itself is a subview of [self view]. Instead, you want to ask [self view] to do the conversion, which should take into account any UINavigationBar's, etc.
CGRect frameOne = [[self view] convertRect:[labelOne frame] toView:nil];
CGRect frameTwo = [[self view] convertRect:[labelTwo frame] toView:nil];

Overlaying a UIView onto a Cocos layer?

I'm new to iOS and Cocos development.
I currently have a basic app going on in my HelloWorldLayer class. It contains my sprites and touch interaction methods and all is well.
I'm trying to add another "panel" (UIView?) over top of what is currently seen. Eventually this panel will have buttons or other things that will interact with the main canvas.
How can I include another UIView onto the canvas screen? Through my appDelegate, or my HelloWorldLayer?
Thanks
Here is one way to do it. I've used UITextView here but you could use the approach for any descendant of UIView. Bear in mind that UIKit's y coordinate is zero at the top-left of the screen, whereas Cocos2D's is zero at the bottom left.
// Make your subview
UITextView* t = [[UITextView alloc] initWithFrame: CGRectMake(10, 10, 200, 200)];
t.backgroundColor = [UIColor blackColor];
t.textColor = [UIColor whiteColor];
t.text = #"Hello UIKit!";
t.editable = NO;
// Add it as a subview of the Cocos2D view
UIView* cocosView = [[CCDirector sharedDirector] openGLView];
[cocosView addSubview:t];
Alternatively you could try Blue Ether's CCUIViewWrapper, repository here.

Resources