iOS iPhone is it possible to clone UIView and have it draw itself to two UIViews? - iphone

I'm thinking of a way to have a UIView render itself onto another UIView as well as the first one. So I have my main UIView with it's bounds, and the UIView also renders itself in some other UIView.
Is this possible ? Does it require extensive layer operations?

Don't know whats your real intention is, but this will draw the view twice, userinteraction etc. will not work on the second view. Also this solution does not take care of different frame sizes.
Header of the View you want to clone
#interface SrcView : UIView
#property(nonatomic, readonly, strong) UIView *cloneView;
#end
#interface CloneView : UIView
#property(nonatomic, weak) UIView *srcView;
- (id)initWithView:(UIView *)src;
#end
implementation of the View you want to clone
#import "SrcView.h"
#import "CloneView.h"
#implementation SrcView
#synthesize cloneView;
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
[cloneView setNeedsDisplay];
}
- (UIView *)cloneView {
if (!cloneView) {
cloneView = [[CloneView alloc] initWithView:self];
}
return cloneView;
}
#end
#implementation CloneView
#synthesize srcView;
- (id)initWithView:(UIView *)src {
self = [super initWithFrame:src.frame];
if (self) {
srcView = src;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[srcView.layer renderInContext:UIGraphicsGetCurrentContext()];
}
#end
now you can just call cloneView and add it somewhere you want.

This seems to be an oft asked question here on StackOverflow. For example:
iphone, ipad Duplicate UIView - Cloned View
Copying the drawn contents of one UIView to another
UIView duplicate
Duplicate, clone or copy UIView
But if it were me doing this, my first approach would be to get a handle to the UIView I want to copy, then recursively iterate all the subviews of it and then copy & add them as subviews to the UIView I want to copy the main UIView into.
I can't imagine there's too much layer operations going on with this, but you would likely need to figure out how to programmatically re-establish outlets and/or actions.

There is no easy way to clone a view and then to update two views by one line of code. Because their underlying CALayers are different. But for duplicating a UIView, here is a new method you can use: Use UIView's method:
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
This is the fastest way to draw a view. Available in iOS 7.

Create another instance of the UIView you wish to "clone" and add it as a subview to another view. You don't really clone a UIView object, you simply create another instance of it.

Related

glkView drawInRect not called

I am learning OpenGLES and I am trying to put a GLKViewer inside an UIViewController.
I know I can come around the main issues by using GLViewController, but I am trying to learn how to do it this way.
I found this question, Nesting GLKView into UIViewController and Nested GLKView and GLKViewController but I must be missing something even though I think I am doing all the right steps because when I run my project, I am not getting to the drawInRect print line.
In the storyboard I am pointing the ViewController as the delegate of the glkview component.
I tried to keep the code as simple as possible and any help will be apreciated:
MyController.h
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
#interface MyGLController : UIViewController <GLKViewDelegate>
{
GLuint vertexBufferID;
}
#property (weak, nonatomic) IBOutlet GLKView *glview;
#property (strong, nonatomic) GLKBaseEffect *baseEffect;
#end
MyGLController.m
#import "MyGLController.h"
#implementation MyGLController
//#synthesize baseEffect;
-(void) viewDidLoad{
[super viewDidLoad];
self.glview.context = [[EAGLContext alloc] initWithAPI:
kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:self.glview.context];
printf("View Loaded");
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
printf("DrawInRect");
}
#end
* Update *
As far as I can tell the glkview is hooked up properly as suggested per and added the josh-knapp and called setNeedsDisplay.
In case there's something I am missing, I have uploaded a copy of the project here: https://github.com/jcrogel/OpenGLDebug.git
I am a total noob in this so I apologize for any silly oversight :)
You didn't say you hooked up the glview property of MyGLController in the storyboard, so verify that.
Next, you're setting up the glview context after it loads. Without a GLKViewController, there is nothing telling the glview it needs to be drawn. Make sure you call [self.glview setNeedsDisplay] somewhere after the context is set up.
I had a similar problem.
By default, xcode sets the enable setNeedsDisplay checkbox to NO in GLKView property list.
Once I changed it, it worked.
I see this question was already answered. But I had the same problem, with a different solution. I figured I'd post the info here on the off chance others might be able to benefit from it.
Problem Description
While using GLKView and GLKViewController, the render loop function (drawInRect) is called once, but not called again.
Possible Cause
Some methods to CViewController have been implemented incorrectly, without calling their supers. The following code illustrates three such functions. The examples below have the three functions implemented correctly, calling their supers.
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:NO];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:NO];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:NO];
}

UIView drawRect Method with options

I want to create an UIView Class that you can initialise with several options.
I think the problem is the drawRect() method. Is it possible to initialise the class with a certain option like
[[MyUIView alloc]initWithColor:[UIColor blueColor]]
I read that I should never call the drawRect() method without initialising an object.
So how can I realize the statement above without calling it manually?
What I have tried so far:
I thought that I could just initialize the View and then call Method
[MyView changeColorTo:green]
which tries to redraw the View however I couldn't get that to work.
Maybe it's important that I draw with coreGraphics (with a certain color, which should be selectable) a rounded rectangle in the drawRect() method.
How can I implement my desired behaviour?
call
[self setNeedsDisplayInRect:aCGRect];
that calls drawRect: for you.
Edit on the color property:
#interface MyView: UIView
#property (nonatomic, strong) UIColor *drawColor;
#end
#implementation MyView
#synthesize drawColor;
- (id)initWithWhatever
{
self.drawColor = [UIColor infraRedColor];
[self setNeedsDisplayInRect:aRect];
}
- (void)drawRect:(CGRect)r
{
// use self.drawColor
}
#end

iOS - Scrolling two UITextViews simultaneously

I've done some looking around but I haven't been able to find anything that clearly explains how I could simultaneously scroll two un-editable UITextViews. I think I may need to use either scrollRangeToVisible, or setContentOffset, though I could not get either of them to work.
Does anyone have any examples/samples, or documentation regarding this that they could point me towards?
EDIT: To clarify, I would like to be able to scroll one UITextView, and have the changes as a result of the scrolling reflected on a second UITextView as well.
Thanks!
Use the UIScrollViewDelegate methods to get information about scroll actions of the first scroll view and then scroll the second programmatically like that:
- (void) scrollViewDidScroll:(UIScrollView *)view1 {
scrollView2.contentOffset = view1.contentOffset;
}
React in
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
and set the other's scrollView setContentVisible according to scrollView.contentOffset.
Just be aware that some methods of UIScrollView will invoke scrollViewDidScroll even if called programmatically. This applies to scrollRangeToVisible and will end up in a loop unless you take action to prevent this loop. I don't think that setContentOffset or setting scrollView2.contentOffset = CGPointMake(..,..) does call scrollViewDidScroll. However, I would not sign this in blood. Be prepared to see a loop and take actions to avoid it. (such as boolean instance variable set before calling setContentOffset and re-set in scrollViewDidScroll followed by return;)
Just continuing with previous answers, to give some more information, I generated this code:
In the interface (.h):
#import <UIKit/UIKit.h>
#interface DoubleTextViewController : UIViewController <UITextViewDelegate>
#property (strong, nonatomic) IBOutlet UITextView *textView1;
#property (strong, nonatomic) IBOutlet UITextView *textView1;
#end
In your implementation (.m):
Use this viewDidLoad function after defining the corresponding properties and global variables.
#import "DoubleTextViewController.h"
#define TEXT_VIEW_1_TAG 1001
#define TEXT_VIEW_2_TAG 1002
#interface DoubleTextViewController () {
BOOL isScrolling;
}
#end
#implementation DoubleTextViewController
#synthesize textView1, textView2;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view..
isScrolling = NO;
[self.textView1 setTag:TEXT_VIEW_1_TAG];
[self.textView2 setTag:TEXT_VIEW_2_TAG];
[self.textView1 setDelegate:self];
[self.textView2 setDelegate:self];
}
And add this function for simultaneous scroll.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (isScrolling) {
return;
}
isScrolling = YES;
if (scrollView.tag == TEXT_VIEW_1_TAG) {
[self.textView2 setContentOffset:scrollView.contentOffset animated:NO];
} else if (scrollView.tag == TEXT_VIEW_2_TAG) {
[self.textView1 setContentOffset:scrollView.contentOffset animated:NO];
}
isScrolling = NO;
}
As proposed by Hermann Klecker, the isScrolling variable stops scrolling loops and makes the user experience nicer. Using the code proposed by Fabian Kreiser makes the scroll stops as soon as the user leaves the finger, making it strange.

Dynamic object creation and release

I have a class that instances some other classes. It's working to control their lifespan, or supposed to ;) A root class managing others which contain events and are subviews of the root view.
I have a lot of graphics involved per view and need to clear things out before loading the next.
Any idea how to unload the current subview and how to load the next subview, while maintaining a named reference to it to use in event handling, from a "touches ended" method?
Thanks
// :)
Assuming that you want to dump the old subview and hold a handle to the new one, this is actually pretty easy. You want something like this:
#interface YourView : UIView
{
// Create an ivar in your class
UIView *_subview;
}
// Propertize it as retain to take care of most of the heavy lifting
#property(nonatomic, retain) UIView *subview;
#end
#implementation YourView
// Map the ivar to the property
#synthesize subview = _subview;
// Call this to put in a new subview
-(void) switchToNewSubview:(UIView*)newSubview
{
// Remove the old subview, set the new one, and if the new one isn't nil
// add it as a subview
[self.subview removeFromSuperview];
self.subview = newSubview;
if(newSubview)
[self addSubview:self.subview];
}
// Don't forget to nil out the subview on dealloc to release it
-(void) dealloc
{
self.subview = nil;
[super dealloc];
}
#end

UIView and UIViewController practice for inheritance

I was just wondering if this approach looks like a good practice for apps with a lot of custom views, for nested PNG graphics and animations that may change based on user interaction. I created a BaseView class that extends UIView
#interface BaseView : UIView {
#protected
BaseViewController *controller;
}
#property (retain) BaseViewController *controller;
#end
and a corresponding controller class which is the primary location which I am putting code to manipulate the view
#interface BaseViewController : UIViewController {
#protected
CGRect drawArea;
}
- (void) drawArea:(CGRect) _drawArea;
- (CGRect) drawArea;
- (void) linkSubviewController: (BaseViewController *) _subviewController;
#end
where "drawArea" is the CGRect used to pass to the view as a frame.
"linkSubviewController" allows you to nest a controller and view as follows :
- (void) linkSubviewController: (BaseViewController *) _subviewController {
[self.view addSubview:[_subviewController view]];
}
In addition I layered another custom pair called "ImageView" and "ImageViewController" which extend BaseView but also store a UIImage and an x,y,w,h
In the "drawRect" drawing methods on views I can check to see if any vars in the self.controller vars have been changed, or assign images, for example :
UIImage *image = [(ImageViewController *)self.controller image];
CGContextDrawImage( ... ) etc
I write most of the loadView methods something like this
- (void)loadView {
ImageView *v = [[ImageView new] initWithFrame:drawArea];
v.controller = self;
self.view = v;
}
The base "initWithFrame" routine contains
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
So I can load a variety of images with transparent backgrounds without having to assign that each time.
I've been able to use this code throughout my app and it seems to make it easy to write a top level class which assembles the layout of custom items. For animations I've been putting them in the controllers and manipulating self.view.layer.
Basically I am looking for feedback, I am new with Objective-C and the IPhone SDK
There are several issues here:
Using [[Classname new] init...] is incorrect usage of new. Using new is short for [[Classname alloc] init] so you are effectively calling init twice.
Views shouldn't really need to know who is controlling them.
Your view is retaining the controller, and since UIViewController retains its view, you have a retain cycle and neither will ever be fully released.
If you want this type of behavior (whereby a view can delegate its drawing to a parent), try creating a DrawDelegate protocol, have your controllers implement that protocol, and in your view subclass have a non-retaining drawDelegate property:
#protocol DrawDelegate
- (void) drawArea:(CGRect)rect;
#end
#interface BaseView : UIView {
id<DrawDelegate> drawDelegate;
}
#property (assign) id<DrawDelegate> drawDelegate;
#end