First question on Stack Overflow, so bear with me! I am new to d3.js, but have been consistently amazed by what others are able to accomplish with it... and almost as amazed by how little headway I've been able to make with it myself! Clearly I'm not grokking something, so I hope that the kind souls here can show me the light.
My intention is to make a connected graph based on the CSV data. My CSV data has different information some of them need to be represented as a nodes, some of them need to be used as an information for ltool-tip. I can read the data from CSV by using this snippet of code:
d3.csv("data/project.csv", function(links) {
var nodesByName = {};
// Create nodes for each unique source and target.
links.forEach(function(link) {
link.source = nodeByName(link.Project);
link.target = nodeByName(link.Problem);
});
Which will connect the node P1, to three nodes Problems (URLs).
The problem comes after, I could not see the label on the selected nodes, How could I use the extract information in the CSV files such as "Date, Summary, URL". Is there any way to use mouse click, when I click the node I need its information appears on SVG board to be used for further analysis, such as showing the URL, short text of the summary, and when the user click on the board it should show the the complete information.
Here is the code that I used from this link
var width = 800,
height = 600;
var svg = d3.select("#visualization")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("svg:g");
var force = d3.layout.force()
.gravity(0.2)
.distance(200)
.charge(-1500)
.size([width, height]);
var container = svg.append("g")
.attr("id", "container");
d3.csv("data/project.csv", function(links) {
var nodesByName = {};
// Create nodes for each unique source and target.
links.forEach(function(link) {
link.source = nodeByName(link.Project);
link.target = nodeByName(link.Problem);
});
// Extract the array of nodes from the map by name.
var nodes = d3.values(nodesByName);
//define a scale for color mapping
var colormapping = d3.scale.ordinal()
.domain([0,nodes.length])
.range(['#A700E6','#D95B96','#F4DA88','#22C1BE','#F24957','#DBEF91','#CF8EE8','#FF9B58','#B8FFC4','#91AEFF','#E873D3','#CCB298']);
//create label node tooltip
var labeltooltip = d3.select("body").append("div")
.attr("class", "labeltooltip")
.style("opacity", 1e-6);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", function() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
});
svg.call(zoom);
//links
var link = container.selectAll(".line")
.data(links)
.enter()
.append("line")
.attr("stroke-width",4)
// .attr("stroke-width",function (d) { return linethickness(d.value); })
.style("stroke", "gray");
// Create the node circles.
var node = container.selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("r", 20)
.attr("fill", function (d,i) {return d3.rgb(colormapping(i)); })
.call(force.drag);
// Start the force layout.
force
.nodes(nodes)
.links(links)
.on("tick", tick)
.start();
node.on("mousemove", function(d) {
labeltooltip.selectAll("p").remove();
labeltooltip.style("left", (d3.event.pageX+15) + "px").style("top", (d3.event.pageY-10) + "px");
labeltooltip.append("p").attr("class", "tooltiptext").html("<span>Id: </span>" + d.Score );
labeltooltip.append("p").attr("class", "tooltiptext").html("<span>Score: </span>" + d.Score);
});
node.on("mouseover", function(d) {
labeltooltip.transition()
.duration(500)
.style("opacity", 1);
link.style('stroke', function(l) {
if (d === l.source || d === l.target)
return d3.rgb('#C20606');
else
return 'gray';
});
link.style('opacity', function(o) {
return o.source === d || o.target === d ? 1 : 0;
});
node.style("opacity", function(o) {
if (o.id != d.id)
return neighboring(d.id, o.id) ? 1 : 0;
});
});
node.on("mouseout", function(d) {
labeltooltip.transition()
.duration(500)
.style("opacity", 1e-6);
link.style('stroke', 'gray');
link.style('opacity', 1);
node.style("opacity", 1);
});
var circletext = node.append("svg:text")
.text(function(d) {return d.name;})
.attr("class","labelText");
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
circletext.attr("x", function(d) { return d.x-25; });
circletext.attr("y", function(d) { return d.y-25;});
}
function nodeByName(name) {
return nodesByName[name] || (nodesByName[name] = {name: name});
}
addZoomMoveIcon("#labelgraph");
});
UPDATE
My CSV data
Project,Problem, Score,Data,Summary,id
p1,Problem1,10,2014-09-04T13:55:05.623-04:00, text text text text, 1
p1,Problem2,5,2014-09-04T13:55:05.623-04:00, text text text text,2
p1,Problem3,11,2014-09-04T13:55:05.623-04:00, text text text text,3
The final results that I am aiming for would be like the following figure:
Where the yellow box should appear on the SVG when the "problem" node clicked.
The first issue you need to address is where to correctly add the node text. Right now you are nesting the text inside the circles, but the text labels need to be siblings of the circles (they do not even need to live right next to each circle). You can replace this:
var circletext = node.append("svg:text")
.text(function(d) {return d.name;})
.attr("class","labelText");
With:
var circletext = container.selectAll("g")
.data(nodes)
.enter()
.append("text").text(function(d) {return d.name})
.attr("class", "labelText");
(Reference: https://www.dashingd3js.com/svg-text-element)
Next though, if you want to display the Score/Data/Summary note that these are attached to the links, not the nodes (there are only three of them, not four). However, at the point of node creation, you can pass this info from the link to the node. Modify the nodeByName function to add the new attributes for the nodes:
function nodeByName(name,score,data,summary) {
return nodesByName[name] || (nodesByName[name] = {name: name, score: score, data: data, summary: summary});
}
Then modify the call to this function when the links are being created:
links.forEach(function(link) {
link.source = nodeByName(link.Project);
link.target = nodeByName(link.Problem,link.Score,link.Data,link.Summary);
});
To get the labeltooltip to display in a stationary spot, and not next to the nodes, you'll need to remove the bits on the node mouseover and mouseout events that move the div around, and probably just add the div to the html instead of dynamically adding it.
Related
I am trying to show an object without tapping on the screen using ARCore. In the basic sample of ARCore Sceneform provided by Google, you need to tap on the screen after it detects the surface.
I want to implement that, AR shows the object without tapping on the screen.
Anchor newAnchor;
for (Plane plane : mSession.getAllTrackables(Plane.class)) {
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING
&& plane.getTrackingState() == TrackingState.TRACKING)
{
newAnchor = plane.createAnchor(plane.getCenterPose());
break;
}
}
I tried this for displaying without tapping on the screen.
if Anyone know how to do this please help me. Thanks in Advance
Although I suggest you place the object when a user taps & where s/he taps on the screen, what you're asking can be achieved like so. (this example is in Kotlin)
Before you begin placing an object, you need to create a ModelRenderable. Declare one #Nullable globally.
private var modelRenderable: ModelRenderable? = null
//Create the football renderable
ModelRenderable.builder()
//get the context of the ARFragment and pass the name of your .sfb file
.setSource(fragment.context, Uri.parse("FootBall.sfb"))
.build()
//I accepted the CompletableFuture using Async since I created my model on creation of the activity. You could simply use .thenAccept too.
//Use the returned modelRenderable and save it to a global variable of the same name
.thenAcceptAsync { modelRenderable -> this#MainActivity.modelRenderable = modelRenderable }
The major chunk of the programming has to be done on the frame's onUpdate method. So you attach a listener for frame updates like so
fragment.arSceneView.scene.addOnUpdateListener(this#MainActivity) //You can do this anywhere. I do it on activity creation post inflating the fragment
now you handle adding an object on the listener.
override fun onUpdate(frameTime: FrameTime?) {
//get the frame from the scene for shorthand
val frame = fragment.arSceneView.arFrame
if (frame != null) {
//get the trackables to ensure planes are detected
val var3 = frame.getUpdatedTrackables(Plane::class.java).iterator()
while(var3.hasNext()) {
val plane = var3.next() as Plane
//If a plane has been detected & is being tracked by ARCore
if (plane.trackingState == TrackingState.TRACKING) {
//Hide the plane discovery helper animation
fragment.planeDiscoveryController.hide()
//Get all added anchors to the frame
val iterableAnchor = frame.updatedAnchors.iterator()
//place the first object only if no previous anchors were added
if(!iterableAnchor.hasNext()) {
//Perform a hit test at the center of the screen to place an object without tapping
val hitTest = frame.hitTest(frame.screenCenter().x, frame.screenCenter().y)
//iterate through all hits
val hitTestIterator = hitTest.iterator()
while(hitTestIterator.hasNext()) {
val hitResult = hitTestIterator.next()
//Create an anchor at the plane hit
val modelAnchor = plane.createAnchor(hitResult.hitPose)
//Attach a node to this anchor with the scene as the parent
val anchorNode = AnchorNode(modelAnchor)
anchorNode.setParent(fragment.arSceneView.scene)
//create a new TranformableNode that will carry our object
val transformableNode = TransformableNode(fragment.transformationSystem)
transformableNode.setParent(anchorNode)
transformableNode.renderable = this#MainActivity.modelRenderable
//Alter the real world position to ensure object renders on the table top. Not somewhere inside.
transformableNode.worldPosition = Vector3(modelAnchor.pose.tx(),
modelAnchor.pose.compose(Pose.makeTranslation(0f, 0.05f, 0f)).ty(),
modelAnchor.pose.tz())
}
}
}
}
}
}
I used one extension method
//A method to find the screen center. This is used while placing objects in the scene
private fun Frame.screenCenter(): Vector3 {
val vw = findViewById<View>(android.R.id.content)
return Vector3(vw.width / 2f, vw.height / 2f, 0f)
}
This is the end result
If you don't want to use hit-testing or button's action methods for placing an object in a real environment, you can use, for instance, a camera's Pose for auto-placement (remember, you have to add an ARAnchor, its corresponding Node and a Renderable in front of the ARCamera i.e. along -Z direction):
if (this.anchorNode == null) {
Session session = arFragment.getArSceneView().getSession();
float[] position = { 0, 0, -0.75 }; // 75 cm away from camera
float[] rotation = { 0, 0, 0, 1 };
Anchor anchor = session.createAnchor(new Pose(position, rotation));
anchorNode = new AnchorNode(anchor);
anchorNode.setRenderable(yourModelRenderable);
anchorNode.setParent(arFragment.getArSceneView().getScene());
}
Hope this helps.
Here is the code snippet if you want to place only one object.. Thanks #clinkz
override fun onUpdate(frameTime: FrameTime?) {
arFragment?.let { fragment ->
fragment.arSceneView?.let { sceneView ->
sceneView.arFrame?.let { frame ->
if (!placed) {
val trackable = frame.getUpdatedTrackables(Plane::class.java).iterator()
if (trackable.hasNext()) {
val plane = trackable.next() as Plane
if (plane.trackingState == TrackingState.TRACKING) {
fragment.planeDiscoveryController?.hide()
val hitTest =
frame.hitTest(frame.screenCenter().x, frame.screenCenter().y)
val hitTestIterator = hitTest.iterator()
if (hitTestIterator.hasNext()) {
val hitResult = hitTestIterator.next()
val modelAnchor = plane.createAnchor(hitResult.hitPose)
val anchorNode = AnchorNode(modelAnchor)
anchorNode.setParent(sceneView.scene)
val transformableNode =
TransformableNode(fragment.transformationSystem)
transformableNode.setParent(anchorNode)
transformableNode.renderable = this#ARActivity.modelRenderable
transformableNode.worldPosition = Vector3(
modelAnchor.pose.tx(),
modelAnchor.pose.compose(
Pose.makeTranslation(
0f,
0.05f,
0f
)
).ty(),
modelAnchor.pose.tz()
)
placed = true
}
}
}
}
}
}
}
}
private fun Frame.screenCenter(): Vector3 {
val vw = findViewById<View>(android.R.id.content)
return Vector3(vw.width / 2f, vw.height / 2f, 0f)
}
I would like to "TOGGLE" between two functions (hide & unhide of a specific column - allocated to a specific month), by clicking on a custom item (monthName) in a menu that I created in Google Sheets (using the Ui).
Currently only the first function (hiding of a column) works. The second function (unhiding that same column), does not work.
Would you please be so kind to help.
Here follows the code:
var month = "Month-YY";
function onOpen() {
var ui = SpreadsheetApp.getUi()
ui.createMenu('Show/Hide Columns')
.addItem(month, 'toggleMonth')
.addToUi();
};
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheets()[0];
var count = 0;
function toggleMonth() {
count++;
var isEven = function(someNumber) {
return (someNumber % 2 === 0) ? true : false;
};
if (isEven(count) === false) {
sh.hideColumns(2, 3)
} else if (isEven(count) === true) {
sh.unhideColumn(sh.getRange('B:D'))
}
};
I have these codes for extracting a datepickerskin from a datepicker and place it in an anchorpane. It works well but my problem is how do i get the value from it? Say, when you click on a number like 8 and get the value as a date, just like the datepicker itself?
Here is my code: zig8 is the anchopane.
DatePickerSkin datePickerSkin = new DatePickerSkin(new DatePicker(LocalDate.now()));
Region pop = (Region)datePickerSkin.getPopupContent();
pop.setPrefHeight(zig8.getPrefHeight());
pop.setPrefWidth(zig8.getPrefWidth());
zig8.getChildren().add(pop);
I tried to attach a mouse event this way:
pop.addEventHandler(MouseEvent.MOUSE_CLICKED,(e)->
{
//a code to get the value.
});
Thanks....
This is probably not very save and could break every time DatePicker gets updated. But lets assume that does not happen :)
If you look at the code of DatePicker, you can see that it structure is roughly:
DatePickerContent (VBox)
MonthYearPane (BorderPane)
GridPane
Contains the Buttons, which are of type DateCell
So we can write a function to search the content of the DatePicker and find all the DateCells. Since they are being reused we need to do this only once. We can then attach an event listener to each DateCell
...
List<DateCell> dateCells = getAllDateCells(pop);
DatePickerSkin datePickerSkin = new DatePickerSkin(new DatePicker(LocalDate.now()));
DatePickerContent pop = (DatePickerContent)datePickerSkin.getPopupContent();
pop.setPrefHeight(zig8.getPrefHeight());
pop.setPrefWidth(zig8.getPrefWidth());
zig8.getChildren().add(pop);
List<DateCell> dateCells = getAllDateCells(pop);
for (DateCell cell : dateCells)
{
cell.addEventHandler(
MouseEvent.MOUSE_PRESSED,(e)->
{
System.out.println("Mouse clicked :" + cell.getItem());
}
);
}
...
private static List<DateCell> getAllDateCells(DatePickerContent content)
{
List<DateCell> result = new ArrayList<>();
for (Node n : content.getChildren())
{
System.out.println("node " + n + n.getClass());
if (n instanceof GridPane)
{
GridPane grid = (GridPane) n;
for (Node gChild : grid.getChildren())
{
System.out.println("grid node: " + gChild + gChild.getClass());
if (gChild instanceof DateCell)
{
result.add((DateCell) gChild);
}
}
}
}
return result;
}
Since DateCell derives from Cell<> it knows its current date and we can use getItem to figure out the date
Alternative:
You could probably get access to the protected DateCell List in DatePickerContent by using Reflection.
protected List<DateCell> dayCells = new ArrayList<DateCell>();
I've started using PDFClown some weeks ago. My purpose is multi-word highlighting, mainly on newspapers. Starting from the org.pdfclown.samples.cli.TextHighlightSample example, I succeeded in extracting multi-word positions and highlighting them. I even solved some problems due to text ordering and matching in most cases.
Unfortunately my framework includes FPDI and it does not consider PDFAnnotations. So, all the content outside of a page content stream, like text annotations and other so called markup annotations, get lost.
So any suggestion on creating "Text Highlighting" with PdfClown and without using PDF annotations?
To not have the highlight in an annotation but instead in the actual page content stream, one has to put the graphic commandos into the page content stream which in case of the org.pdfclown.samples.cli.TextHighlightSample example are implicitly put into the normal annotation appearance stream.
This can be implemented like this:
org.pdfclown.files.File file = new org.pdfclown.files.File(resource);
Pattern pattern = Pattern.compile("S", Pattern.CASE_INSENSITIVE);
TextExtractor textExtractor = new TextExtractor(true, true);
for (final Page page : file.getDocument().getPages())
{
final List<Quad> highlightQuads = new ArrayList<Quad>();
Map<Rectangle2D, List<ITextString>> textStrings = textExtractor.extract(page);
final Matcher matcher = pattern.matcher(TextExtractor.toString(textStrings));
textExtractor.filter(textStrings, new TextExtractor.IIntervalFilter()
{
#Override
public boolean hasNext()
{
return matcher.find();
}
#Override
public Interval<Integer> next()
{
return new Interval<Integer>(matcher.start(), matcher.end());
}
#Override
public void process(Interval<Integer> interval, ITextString match)
{
{
Rectangle2D textBox = null;
for (TextChar textChar : match.getTextChars())
{
Rectangle2D textCharBox = textChar.getBox();
if (textBox == null)
{
textBox = (Rectangle2D) textCharBox.clone();
}
else
{
if (textCharBox.getY() > textBox.getMaxY())
{
highlightQuads.add(Quad.get(textBox));
textBox = (Rectangle2D) textCharBox.clone();
}
else
{
textBox.add(textCharBox);
}
}
}
highlightQuads.add(Quad.get(textBox));
}
}
#Override
public void remove()
{
throw new UnsupportedOperationException();
}
});
// Highlight the text pattern match!
ExtGState defaultExtGState = new ExtGState(file.getDocument());
defaultExtGState.setAlphaShape(false);
defaultExtGState.setBlendMode(Arrays.asList(BlendModeEnum.Multiply));
PrimitiveComposer composer = new PrimitiveComposer(page);
composer.getScanner().moveEnd();
// TODO: reset graphics state here.
composer.applyState(defaultExtGState);
composer.setFillColor(new DeviceRGBColor(1, 1, 0));
{
for (Quad markupBox : highlightQuads)
{
Point2D[] points = markupBox.getPoints();
double markupBoxHeight = points[3].getY() - points[0].getY();
double markupBoxMargin = markupBoxHeight * .25;
composer.drawCurve(new Point2D.Double(points[3].getX(), points[3].getY()),
new Point2D.Double(points[0].getX(), points[0].getY()),
new Point2D.Double(points[3].getX() - markupBoxMargin, points[3].getY() - markupBoxMargin),
new Point2D.Double(points[0].getX() - markupBoxMargin, points[0].getY() + markupBoxMargin));
composer.drawLine(new Point2D.Double(points[1].getX(), points[1].getY()));
composer.drawCurve(new Point2D.Double(points[2].getX(), points[2].getY()),
new Point2D.Double(points[1].getX() + markupBoxMargin, points[1].getY() + markupBoxMargin),
new Point2D.Double(points[2].getX() + markupBoxMargin, points[2].getY() - markupBoxMargin));
composer.fill();
}
}
composer.flush();
}
file.save(new File(RESULT_FOLDER, "multiPage-highlight-content.pdf"), SerializationModeEnum.Incremental);
(HighlightInContent.java method testHighlightInContent)
You will recognize the text extraction frame from the original example. Merely now the quads from a whole page are collected before they are processed, and the processing code (which mostly has been borrowed from TextMarkup.refreshAppearance()) draws forms representing the quads into the page content.
Beware, to make this work generically, the graphics state has to be reset before inserting the new instructions (the position is marked with a TODO comment). This can be done either by applying save/restore state or by actually counteracting unwanted changed state entries. Unfortunately I did not see how to do the former in PDF Clown and have not yet had the time to do the latter.
I have made a Xcode project for a tabbed application that displays images of color swatches in a Scrollview. How do I link One of the images in my scrollview to go to the next View Controller? Below is my code and pictures. So when you click on one of the images or swatch color in the scrollview it links to the New Controller.
I have multiple images that scroll down the page of the iPhone, Do I have to loop the images cause there are 24 images. I was able to make one button and link it to the next scene with the interface builder, But I can fit 5 images on the screen..
DecorsViewController_iPhone.h
#import <UIKit/UIKit.h>
#interface DecorsViewController_iPhone : UIViewController
{
IBOutlet UIScrollView *scrollViewDecors;
}
#property (nonatomic, retain) UIView *scrollViewDecors;
#end
DecorsViewController_iPhone.m
#import "DecorsViewController_iPhone.h"
#interface DecorsViewController_iPhone ()
#end
#implementation DecorsViewController_iPhone
#synthesize scrollViewDecors;
const CGFloat kScrollObjHeight = 81.5;
const CGFloat kScrollObjWidth = 320.0;
const NSUInteger kNumImages = 24;
- (void)layoutScrollImages
{
UIImageView *view = nil;
NSArray *subviews = [scrollViewDecors subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
CGFloat curYLoc = 0;
CGFloat curYSpace = 1;
for (view in subviews)
{
if ([view isKindOfClass:[UIImageView class]] && view.tag > 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, curYLoc);
view.frame = frame;
curYLoc += (curYSpace + kScrollObjHeight);
}
}
// set the content size so it can be scrollable
[scrollViewDecors setContentSize:CGSizeMake(([scrollViewDecors bounds].size.width), (kNumImages * kScrollObjHeight))]; // Vertical Option
}
- (void)viewDidLoad
{
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
// 1. setup the scrollview for multiple images and add it to the view controller
//
// note: the following can be done in Interface Builder, but we show this in code for clarity
[scrollViewDecors setBackgroundColor:[UIColor blackColor]];
[scrollViewDecors setCanCancelContentTouches:NO];
scrollViewDecors.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollViewDecors.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
scrollViewDecors.scrollEnabled = YES;
// pagingEnabled property default is NO, if set the scroller will stop or snap at each photo
// if you want free-flowing scroll, don't set this property.
// scrollView1.pagingEnabled = YES;
// load all the images from our bundle and add them to the scroll view
NSUInteger i;
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:#"Artwork_iPhone_Decors_Scrollview_%d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = imageView.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
imageView.frame = rect;
imageView.tag = i; // tag our images for later use when we place them in serial fashion
[scrollViewDecors addSubview:imageView];
//[imageView release];
}
[self layoutScrollImages]; // now place the photos in serial layout within the scrollview
}
//- (void)dealloc
//{
// [scrollViewDecors release];
//
// [super dealloc];
//}
//- (void)viewDidLoad
//{
// [super viewDidLoad];
// // Do any additional setup after loading the view, typically from a nib.
//}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
Firstly, you need to add a gesture recogniser to your view with something like:
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(flipView:)];
[singleTapGestureRecognizer setNumberOfTapsRequired:1];
[self.view addGestureRecognizer:singleTapGestureRecognizer];
You then need to implement the 'flipView' method:
- (void)flipView:(UIPanGestureRecognizer *)recognizer {
[self performSegueWithIdentifier:#"textView" sender:self];
}
As you can see in my case, when the method is triggered I perform a segue (see below):
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"textView"])
{
//---Pass text to view
CGRect visibleRect;
visibleRect.origin = scrollView.contentOffset;
visibleRect.size = scrollView.bounds.size;
int number;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
number = visibleRect.origin.x / 768;
}
else {
number = visibleRect.origin.x / 320;
}
TextViewController *textViewController = segue.destinationViewController;
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
textViewController.text = [appDelegate.textArray objectAtIndex:number];
}
}
The 'number' variable is used to decide which image has been clicked, I divide the visible rects origin by the width of the screen in pixels to calculate which image has been pressed and therefore what data to send to my new view.
In your case, you would use the Y coordinate to perform a calculation to decide which colour has been pressed, possibly something like:
int number;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
number = visibleRect.origin.y / <height of each image on iPad in pixels>;
}
else {
number = visibleRect.origin.y / <height of each image on iPhone in pixels>;
}
Alternatively, you could use a UITableView and just populate the cells with the appropriate colour and then display the new view based on which cell was pressed.
EDIT
Use a UITableView, and populate each cell with your image using custom tableview cells. This is a much easier approach than what you are suggesting.
See these links:
adding images to UItableView
If using iOS 5 -
http://kurrytran.blogspot.co.uk/2011/10/ios-5-storyboard-uitableview-tutorial.html
if not -
http://www.iosdevnotes.com/2011/10/uitableview-tutorial/
You are overcomplicating this task massively, follow the above tutorials and you'll be done in no time. If this answer helped you, please mark it as correct.
You can customize your UIImageView. For example, MyImageView. Set its delegate to your main ScrollView and add UITapGestureRecognizer and TapDetecting Delegate methods.
You can implement in the MyImageView:
- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer {
// single tap
if ([_delegate respondsToSelector:#selector(myImageViewWasSingleTapped:)])
[_delegate myImageViewWasSingleTapped:self];
}
then in your main ScrollView(delegate of MyImageView):
- (void)myImageViewWasSingleTapped:(MyImageView *)miv {
AnotherViewController *asv = [[AnotherViewController alloc] initWithImage: miv];
[self.navigationController pushViewController: asv animated:YES];
[asv release];
}
You can use a button instead of a UIImageView, and set the button's image to your image. Your loop in viewDidLoad would be like this:
// load all the images from our bundle and add them to the scroll view
NSUInteger i;
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:#"Artwork_iPhone_Decors_Scrollview_%d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIButton *imageButton = [[UIButton alloc] init];
[imageButton setImage:image forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(pushNextViewController:) forControlEvents:UIControlEventTouchUpInside];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = CGRectMake(0, 0, kScrollObjWidth, kScrollObjHeight);
imageButton.frame = rect;
imageButton.tag = i; // tag our images for later use when we place them in serial fashion
[scrollViewDecors addSubview:imageButton];
// Release imageButton unless you're using ARC
}
And then you'd need to define an action for imageButton, in this case I called it pushNextViewController:
- (void)pushNextViewController:(id)sender
{
UIViewController *yourNextViewController = // initialise your next view controller here
[self.navigationController pushViewController:yourNextViewController animated:YES];
}