iOS Problem with moving a view

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
Firstly, just a quick hello to the community!

I'm new this week to IPhone app development, and Macs in general; my obj c skills are new (it's been some 15 years since I dabbled in C), but I have many years in some other languages (ColdFusion, SQL etc).

I've read a lot of recommended documentation, and jumped in the deep end with my first application; perhaps somewhat silly but that's how I've learned languages in the past!

Anyway, on with my problem - no doubt it's something utterly basic but I've been pulling my hair out for 2 days trying to find (or work out) an answer.

I'm trying to get a loaded image to move in response to a basic finger touch; I have borrowed the code from the "moveme" sample application and I have tried to integrate it into my existing project (which interface has been implemented through IB), however I am stuck on the one error and cant resolve it. TubeMap is the name of the object view holding the image (which is bigger than the view that holds it), which I have made of the class MoveMeView, which I believe is a subclass of UIImageView.

MoveMeView.h

#import <UIKit/UIKit.h>

@class TubeMap;

@interface MoveMeView : UIView {
TubeMap *tubeMap;
}

@property (nonatomic, retain) TubeMap *tubeMap;

@end
MoveMeView.m
#import "MoveMeView.h"

@implementation MoveMeView

@synthesize tubeMap;


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

// We only support single touches, so anyObject retrieves just that touch from touches
UITouch *touch = [touches anyObject];

if ([touch view] != tubeMap) {
return;
}
}


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

UITouch *touch = [touches anyObject];

// If the touch was in the tubeMap, move the tubeMap accordingly
if ([touch view] == tubeMap) {
CGPoint location = [touch locationInView:self];
tubeMap.center = location;

return;
}
}
On compile, 1 error and 2 warnings

- tubeMap with a "comparison of distinct Objective-C types lacks a cast" (both warnings)
and more importantly
tubeMap.center = location; gives "request for member 'center' in something not a structure or union".

Now I think that means I'm trying to set a variable in my instance of TubeMap that doesn't exist, but it should be the centre of the image. So no doubt I'm doing something basic incorrectly. If anyone could help me out (a fix would be nice but an explanation of what I've done wrong would be even more helpful) I would be most grateful.

Thanks in advance!
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,638
61
Harrogate
The compiler will have marked the offending rows in your editor window. That should tell you where to fix it. It would help us if you told us which lines it highlighted.

Anyway...

- tubeMap with a "comparison of distinct Objective-C types lacks a cast" (both warnings)
This is probably on the lines like
Code:
if ([touch view] != tubeMap)
as you are comparing a UIView with a TubeMap (which is a UIView subclass). If you want to get rid of this case tubeMap to (UIView *).

tubeMap.center = location; gives "request for member 'center' in something not a structure or union".
Edit: remove rubish about UIViews not have a center property!

This tells you that neither tubeMap (class TubeMap) or it's superclasses have a center property. Did you mean to use self instead? MoveMeView is a UIView so instances of that have a center property...
 

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
Add this to the top of MoveMeView.m

#import "TubeMap.h"

gcc's error messages suck dead donkey (well you know). This error means that gcc doesn't know what TubeMap is.
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
Thanks both of you for getting back so promptly; this has been very helpful to aid my understanding a little more but I'm still having kittens!

It would help us if you told us which lines it highlighted.
Sorry I will do that next time; however you estimated correctly where they were.

Code:
if ([touch view] != tubeMap)
as you are comparing a UIView with a TubeMap (which is a UIView subclass). If you want to get rid of this case tubeMap to (UIView *).
I wanted to compare it with the instance of TubeMap (so if the screen is touched within this view then the method does something). If I change tubeMap to if ([touch view] != (UIView *tubeMap)) I get a syntax error before 'tubeMap', which obviously means I'm still not understanding something fundamental.

This tells you that neither tubeMap (class TubeMap) or it's superclasses have a center property. Did you mean to use self instead? MoveMeView is a UIView so instances of that have a center property...
Ah thanks - at least I was right about what the error was trying to say, even if I don't get why. Am I right in assuming that the class and superclasses don't have a center property because it's the instance of the view that does? I'm trying to get the instance of TubeMap that is on the screen to re-center under the user's touch, displaying the part of the graphic in the view that is currently clipped offscreen.

Just FYI when I do reference it to self it compiles, but then doesn't display in the simulator anyway....and I thought this would be an easy way to learn...:D

Add this to the top of MoveMeView.m

#import "TubeMap.h"

gcc's error messages suck dead donkey (well you know). This error means that gcc doesn't know what TubeMap is.
Sadly there is no TubeMap.h - TubeMap is a view that is made by ForthView.xib, which is one of 4 nibs that I've built to house 4 tab options of the app. It loads fine without including moveme.m and .h in the project, but when I change the final error in moveme.m from tubeMap.center to self.center the view is white-screen (no crash) for that tab - I think at this point IB is actually causing me a lot of pain as if I'd just written it programatically it would of been a huge amount clearer.

ForthView.xib
Code:
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.02">
	<data>
		<int key="IBDocument.SystemTarget">512</int>
		<string key="IBDocument.SystemVersion">9D34</string>
		<string key="IBDocument.InterfaceBuilderVersion">670</string>
		<string key="IBDocument.AppKitVersion">949.33</string>
		<string key="IBDocument.HIToolboxVersion">352.00</string>
		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
			<bool key="EncodedWithXMLCoder">YES</bool>
			<integer value="7"/>
		</object>
		<object class="NSArray" key="IBDocument.PluginDependencies">
			<bool key="EncodedWithXMLCoder">YES</bool>
			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
		</object>
		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
			<bool key="EncodedWithXMLCoder">YES</bool>
			<object class="IBProxyObject" id="372490531">
				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
			</object>
			<object class="IBProxyObject" id="975951072">
				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
			</object>
			<object class="IBUIImageView" id="979351559">
				<reference key="NSNextResponder"/>
				<int key="NSvFlags">292</int>
				<string key="NSFrameSize">{240, 128}</string>
				<reference key="NSSuperview"/>
				<bool key="IBUIOpaque">NO</bool>
				<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
				<int key="IBUIContentMode">2</int>
				<bool key="IBUIMultipleTouchEnabled">YES</bool>
				<object class="NSCustomResource" key="IBUIImage">
					<string key="NSClassName">NSImage</string>
					<string key="NSResourceName">LUM.png</string>
				</object>
			</object>
		</object>
		<object class="IBObjectContainer" key="IBDocument.Objects">
			<object class="NSMutableArray" key="connectionRecords">
				<bool key="EncodedWithXMLCoder">YES</bool>
				<object class="IBConnectionRecord">
					<object class="IBCocoaTouchOutletConnection" key="connection">
						<string key="label">view</string>
						<reference key="source" ref="372490531"/>
						<reference key="destination" ref="979351559"/>
					</object>
					<int key="connectionID">11</int>
				</object>
			</object>
			<object class="IBMutableOrderedSet" key="objectRecords">
				<object class="NSArray" key="orderedObjects">
					<bool key="EncodedWithXMLCoder">YES</bool>
					<object class="IBObjectRecord">
						<int key="objectID">0</int>
						<object class="NSArray" key="object" id="360949347">
							<bool key="EncodedWithXMLCoder">YES</bool>
						</object>
						<reference key="children" ref="1000"/>
						<nil key="parent"/>
					</object>
					<object class="IBObjectRecord">
						<int key="objectID">-1</int>
						<reference key="object" ref="372490531"/>
						<reference key="parent" ref="360949347"/>
						<string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
					</object>
					<object class="IBObjectRecord">
						<int key="objectID">-2</int>
						<reference key="object" ref="975951072"/>
						<reference key="parent" ref="360949347"/>
					</object>
					<object class="IBObjectRecord">
						<int key="objectID">7</int>
						<reference key="object" ref="979351559"/>
						<reference key="parent" ref="360949347"/>
						<string key="objectName">TubeMap</string>
					</object>
				</object>
			</object>
			<object class="NSMutableDictionary" key="flattenedProperties">
				<bool key="EncodedWithXMLCoder">YES</bool>
				<object class="NSMutableArray" key="dict.sortedKeys">
					<bool key="EncodedWithXMLCoder">YES</bool>
					<string>-1.CustomClassName</string>
					<string>-2.CustomClassName</string>
					<string>7.CustomClassName</string>
					<string>7.IBEditorWindowLastContentRect</string>
					<string>7.IBPluginDependency</string>
				</object>
				<object class="NSMutableArray" key="dict.values">
					<bool key="EncodedWithXMLCoder">YES</bool>
					<string>FirstViewController</string>
					<string>UIResponder</string>
					<string>MoveMeView</string>
					<string>{{752, 803}, {240, 128}}</string>
					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
				</object>
			</object>
			<object class="NSMutableDictionary" key="unlocalizedProperties">
				<bool key="EncodedWithXMLCoder">YES</bool>
				<object class="NSArray" key="dict.sortedKeys">
					<bool key="EncodedWithXMLCoder">YES</bool>
				</object>
				<object class="NSMutableArray" key="dict.values">
					<bool key="EncodedWithXMLCoder">YES</bool>
				</object>
			</object>
			<nil key="activeLocalization"/>
			<object class="NSMutableDictionary" key="localizations">
				<bool key="EncodedWithXMLCoder">YES</bool>
				<object class="NSArray" key="dict.sortedKeys">
					<bool key="EncodedWithXMLCoder">YES</bool>
				</object>
				<object class="NSMutableArray" key="dict.values">
					<bool key="EncodedWithXMLCoder">YES</bool>
				</object>
			</object>
			<nil key="sourceID"/>
			<int key="maxID">11</int>
		</object>
		<object class="IBClassDescriber" key="IBDocument.Classes">
			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
				<bool key="EncodedWithXMLCoder">YES</bool>
				<object class="IBPartialClassDescription">
					<string key="className">FirstViewController</string>
					<string key="superclassName">UIViewController</string>
					<object class="IBClassDescriptionSource" key="sourceIdentifier">
						<string key="majorKey">IBProjectSource</string>
						<string key="minorKey">Classes/FirstViewController.h</string>
					</object>
				</object>
				<object class="IBPartialClassDescription">
					<string key="className">MoveMeView</string>
					<string key="superclassName">UIImageView</string>
					<object class="IBClassDescriptionSource" key="sourceIdentifier">
						<string key="majorKey">IBProjectSource</string>
						<string key="minorKey">Classes/MoveMeView.h</string>
					</object>
				</object>
				<object class="IBPartialClassDescription">
					<string key="className">UIImageView</string>
					<string key="superclassName">UIView</string>
					<object class="IBClassDescriptionSource" key="sourceIdentifier">
						<string key="majorKey">IBUserSource</string>
						<string key="minorKey"/>
					</object>
				</object>
			</object>
		</object>
		<int key="IBDocument.localizationMode">0</int>
		<string key="IBDocument.LastKnownRelativeProjectPath">ITube.xcodeproj</string>
		<int key="IBDocument.defaultPropertyAccessControl">3</int>
	</data>
</archive>
Any further thoughts guys?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,638
61
Harrogate
Your UIView cast is wrong. It should be:

Code:
((UIView *) tubeMap)
not

Code:
(UIView* tubeMap)
What is TubeMap? A custom UIView subclass? If so there has to be code for this to exist. Simply changing the class to this in IB will not work: you need to have IB create the skeleton files and implement the class.
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
Your UIView cast is wrong. It should be:

Code:
((UIView *) tubeMap)
not

Code:
(UIView* tubeMap)
What is TubeMap? A custom UIView subclass? If so there has to be code for this to exist. Simply changing the class to this in IB will not work: you need to have IB create the skeleton files and implement the class.
Ah thanks - I knew I was missing something fundamental about how to cast!

That still returns an error, but I think were actually getting to the problem now, and it looks like my lack of understanding about IB.

TubeMap is the name which I gave the view object in ForthView.xib and then changed it's class type from UIImageView to MoveMeView. Since it compiles ok and runs fine when I remove movemeview.h and .m and revert the class to UIImageView, I had assumed that the IB file created an object instance with that name, which must have a class, so I have been trying to get movemeview to control the instance of TubeMap. I hope that makes sense somehow...?
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,638
61
Harrogate
Style note: don't start instance variable names with a capital: everyone else will read that as a class name.

What do you mean by "TubeMap is the name which I gave the view object". Objects don't have names.
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
Style note: don't start instance variable names with a capital: everyone else will read that as a class name.

What do you mean by "TubeMap is the name which I gave the view object". Objects don't have names.
I thought it was a class name.

Ok, I'm not using the right terminology here I think and causing more confusion; my apologies.

In IB you can drag in a UIImageView and set some properties. I did this, and named it TubeMap. I then set a graphic file that is bigger than the view to load into it, aspect fill. I set it's ref outlet view to file's owner and compiled my project. It works as intended, displaying part of the image onscreen when the appropriate tab was pressed to load this nib file.

I then decided I wanted to try to understand how touch worked, so I attempted to alter the movemeview example to manage a user's touch in this view to move the graphic on the screen to show areas that were currently clipped by the static image.

So I tried to alter movemeview.h and .m to refer to the UIImageview I had created in my nib file (named TubeMap), and in IB I set it's type from UIImageView to MoveMeView, as I thought MoveMeView was a custom subclass of UIImageview and would thus use any MoveMeView methods "automatically" (ie touchesBegan and touchesMoved to move TubeMap).

This to me seemed logical, but clearly it's not :confused: I hope that explains what I was trying to do and can highlight my basic assumption error(s)!
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,638
61
Harrogate
If TubeMap is a class then you need implementation files (.m and .h) for it. But I am still confused about this "and named it TubeMap". Views don't have names. You can't assign a string to a view as it's name and then use that as some sort of global identifier for the view.

Post a screenshot of where you set the name. I suspect you set the classname of the object. This means you have told IB that you will be using a custom UIView subclass for this view: you must provide code for this custom subclass.
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
If TubeMap is a class then you need implementation files (.m and .h) for it. But I am still confused about this "and named it TubeMap". Views don't have names. You can't assign a string to a view as it's name and then use that as some sort of global identifier for the view.

Post a screenshot of where you set the name. I suspect you set the classname of the object. This means you have told IB that you will be using a custom UIView subclass for this view: you must provide code for this custom subclass.

Ah ok then this is where my assumption is falling down; I assumed you could!

Attached SS is where I am setting the name; how would I reference it then if not by name?

BTW thanks very much for your continued help; it's very much appreciated!
 

Attachments

robbieduncan

Moderator emeritus
Jul 24, 2002
24,638
61
Harrogate
Attached SS is where I am setting the name; how would I reference it then if not by name?
No attachement :confused:

Edit: and now there is :)

Edit again: that name is only usable from Interface Builder (I think). It certainly doesn't translate to a global variable in XCode. It may be possible to programatically step through the view tree querying names, or even ask a UIWindow for an object by name like you can by tag.

The normal way to get a reference to a view would be to have an IBOutlet in the file owner (custom class) that will get linked to the object it's connected to when the nib/xib is loaded.

Also as a side-note you do know you can't distribute the tube map image without paying a license fee to TFL right?

Yet another edit: UIView is just like it's NS counterparty: you can search for subviews by tag but there is no corresponding by name search. That name is only really to help searches in IB I think.
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
No attachement :confused:

Edit: and now there is :)

Edit again: that name is only usable from Interface Builder (I think). It certainly doesn't translate to a global variable in XCode. It may be possible to programatically step through the view tree querying names, or even ask a UIWindow for an object by name like you can by tag.

The normal way to get a reference to a view would be to have an IBOutlet in the file owner (custom class) that will get linked to the object it's connected to when the nib/xib is loaded.

Also as a side-note you do know you can't distribute the tube map image without paying a license fee to TFL right?

Yet another edit: UIView is just like it's NS counterparty: you can search for subviews by tag but there is no corresponding by name search. That name is only really to help searches in IB I think.
Ahhh thanks very much indeed; that explains why I felt like I was banging my head against a tree! I'll do some more research and try to implement it via an IBOutlet then.

And yes - the project is a home one to teach me rather than to distribute - though if I ever finish the whole app I might think about drawing my own map image :D
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
Another day, another problem with this it seems!

Ok, I read up and implemented an IBOutlet and custom class as suggested. Pleased to say the project now compiles and does not crash so thanks very much indeed - however, when I click on the tab to load up this view in the simulator, it doesn't draw the view.

No doubt again it's something fundamental on my part, but despite spending the evening last night trying to spot it, I can't see why. Any further help along my road of learning therefore again much appreciated!

moveme.h

Code:
#import <UIKit/UIKit.h>

@class MoveMeView;

@interface MoveMeView : UIView {
		IBOutlet UIImageView *mapPosition;
}

@property (nonatomic, retain) UIImageView *mapPosition;

@end
moveme.m
Code:
#import "MoveMeView.h"

@implementation MoveMeView

@synthesize mapPosition;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	
	// We only support single touches, so anyObject retrieves just that touch from touches
	UITouch *touch = [touches anyObject];
	
	if ([touch view] != mapPosition) {
				return;
	}
}


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
	
	UITouch *touch = [touches anyObject];
	
	// If the touch was in the tubeMap, move the tubeMap accordingly
	if ([touch view] == mapPosition) {
		CGPoint location = [touch locationInView:self];
		mapPosition.center = location;
		
		return;
	}
}




- (void)dealloc {
	[mapPosition release];
	[super dealloc];	
}

@end
and finally a ss of the view with its settings in IB...

Many thanks!
 

Attachments

robbieduncan

Moderator emeritus
Jul 24, 2002
24,638
61
Harrogate
MoveMeView must either have a draw method or a subview. Is the view that contains the map added as a subview?

Also please use code tags!
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
MoveMeView must either have a draw method or a subview. Is the view that contains the map added as a subview?

Also please use code tags!
Sorry about the code wrappers - I was fixing them as you were posting - your just too fast responding for me to even fix my post errors! :D

Ok, it doesn't, but that brings me back to my initial quandary I think then about accessing views, which was why I was trying to name it in IB - from your reply earlier, am I right in assuming i should be adding [contentView addSubview: *<id_of_the_view_holding_the_image_file>]; (rather than a string holding a name reference) after the synthesize line to add it as a subview? If so, how do I find out what that id number should be?

Thanks to your very kind help I think I may be starting to find may way through this forest!
 

robbieduncan

Moderator emeritus
Jul 24, 2002
24,638
61
Harrogate
I was assuming you'd add the subview in IB. If you are trying to add it in code you need a pointer to that object as per usual. How you get that pointer depends largely on where the object is created. If you are creating it in code you have the pointer: it's up to you how you pass this reference around to get it where you need it. If you create the view in IB but don't add it as a subview then an IBOutlet would be the easiest way.

Personally in my iPhone projects so far I've given up on using IB at all: I just create the entire interface programatically.
 

cjhsb

macrumors newbie
Original poster
Sep 17, 2008
10
0
London
I was assuming you'd add the subview in IB. If you are trying to add it in code you need a pointer to that object as per usual. How you get that pointer depends largely on where the object is created. If you are creating it in code you have the pointer: it's up to you how you pass this reference around to get it where you need it. If you create the view in IB but don't add it as a subview then an IBOutlet would be the easiest way.

Personally in my iPhone projects so far I've given up on using IB at all: I just create the entire interface programatically.
Ah ok yes I did add it in IB; I think I'm starting to understand now. Equally, I think my hatred of IB is growing at the same rate. Many thanks again for helping me along my first steps!