Hey guys I'm trying to implement a scroll with page control, like the iPhone home screen, but in a uitableview cell.
What I tried to do to attempt this is create a custom table view cell with a xib file, and placed the uipagecontrol and uiscrollview on it, and connected it with iboutlets to the uitableviewcell.
This is the code in the .h file for the custom cell.
#interface ScrollableCell : UITableViewCell <UIScrollViewDelegate>
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) IBOutlet UIPageControl *pageControl;
#property (nonatomic, strong) NSMutableArray *viewControllers;
This is the code from the .m file for the custom cell after synthesizing the properties.
- (CGSize)contentSizeForPagingScrollView {
CGRect bounds = self.scrollView.bounds;
return CGSizeMake(bounds.size.width * [self.viewControllers count] , bounds.size.height );
}
#- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
UIView *blueView = [[UIView alloc] initWithFrame:CGRectZero];
[blueView setBackgroundColor:[UIColor blueColor]];
UIView *redView = [[UIView alloc] initWithFrame:CGRectZero];
[redView setBackgroundColor:[UIColor redColor]];
UIView *yellowView = [[UIView alloc] initWithFrame:CGRectZero];
[yellowView setBackgroundColor:[UIColor yellowColor]];
self.viewControllers = [NSArray arrayWithObjects:blueView, redView, yellowView,nil];
for (UIView *view in self.viewControllers) {
[view setFrame:CGRectMake([self.viewControllers indexOfObject:view]*self.scrollView.frame.size.width+5,
5,
self.scrollView.frame.size.width-10,
self.scrollView.frame.size.height-10.0)];
[self.scrollView addSubview:view];
}
self.pageControl.numberOfPages = [self.viewControllers count];
self.pageControl.currentPage = 0;
self.scrollView.pagingEnabled = YES;
self.scrollView.backgroundColor = [UIColor blackColor];
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.contentSize = [self contentSizeForPagingScrollView];
self.scrollView.delegate = self;
[self.contentView addSubview:self.scrollView];
[self.contentView addSubview:self.pageControl];
}
return self;
}
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
Hope you guys can help me get this working. If there is a different approach/ better approach that what I'm attempting, let me know.
For visual reference of what I'm trying to achieve, i think the pulse news app fits the bill
http://itunes.apple.com/us/app/pulse-news-for-iphone/id377594176?mt=8
its a table view, with horizontal scrolling on each cell.
Thanks.
If there is a different approach/ better approach that what I'm attempting, let me know.
For visual reference of what I'm trying to achieve, i think the pulse news app fits the bill.
If your main reference for designing this is the Pulse app, you should know that the developers actually did NOT resort to scrollviews. The Pulse app is all tableviews. One main vertical tableview and several horizontal "hacked" tableviews inside each cell that composes the vertical one.
Its quite a smart implementation for a tableview. But who better to explain this to you than the developers of Pulse themselves; they were invited to a lecture at Stanford for their iOS programming course.
Its on iTunes U and it's at 6 min in the lecture called 10 Hacks to Go From Idea To #1 App. You should watch it and maybe rethink your app design if it suits you better.
Related
I'm guessing I don't know enough about Quartz or CAShapeLayer manipulations, but I wanted to know how to change the fill color to custom UIView that I have.
Here's the implementation for DotView:
#define kCustomBlue [UIColor colorWithRed:181.0/255 green:228.0/255 blue:226.0/255 alpha:1]
#implementation DotView
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = [UIColor clearColor];
self.opaque = YES;
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if ((self = [super initWithCoder:coder])) {
self.backgroundColor = [UIColor clearColor];
self.opaque = YES;
}
return self;
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
[circleLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:rect] CGPath]];
circleLayer.fillColor = kCustomBlue.CGColor;
[self.layer addSublayer:circleLayer];
}
#end
After placing a DotView object ((nonatomic, weak) IBOutlet in my ViewController interface) in my Storyboard, I run the code and everything is fine and dandy. That object in my ViewController interface is called dot1
I want to have a property #property (nonatomic, strong) UIColor* fillingColor; which sets the fill color for the view. How do I implement that correctly?
The idea is this: there is a tap gesture recognizer object attached to the dot1 view, and everytime I tap the dot, the color changes from blue to black (then black to blue).
I'm using XCode 9.3 and have an iPhone 7 running iOS 11.2
Thanks, Anthony
Add the property to the .h:
#interface DotView: UIView
// Add this to everything else you have
#property (nonatomic, strong) UIColor* fillingColor;
#end
Then in the .m, override the setter:
- (void)setFillingColor:(UIColor *)color {
_fillingColor = color;
[self setNeedsDisplay];
}
Then in drawRect:, use your fillingColor property for the fill color:
circleLayer.fillColor = self.fillingColor.CGColor;
Please note that you do not want to be adding layers over and over every time drawRect: is called. You don't even need to use layers for this. Just fill the UIBezierPath.
I'm working with Text Kit in iOS 7 and I'm finding a lot of oddities around NSTextContainer exclusion zones.
I've got two views: a UITextView and a simple draggable UIView; as the UIView moves, I create a bezier path from the UIView's frame (adjusted to within the UITextView's coordinate space) and I update the UITextView's NSTextContainer's exclusionPaths array - pretty straightforward.
In the first screenshot, you can see that Text Kit nicely wraps text around a rectangular exclusion zone:
However, when the user introduces newlines into the UITextView, TextKit seems to think that the exclusion zone is much bigger vertically - by what appears to be exactly as high as the whitespace created by the newline. The bezier path is exactly the same, so this seems to be a Text Kit issue (unless I'm doing something wrong).
Code:
ViewController.h:
#interface ViewController : UIViewController<UITextViewDelegate>
#property (nonatomic, strong) IBOutlet UITextView *textView;
#property (nonatomic, strong) IBOutlet UIView *dragView;
#end
ViewController.m:
-(void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[self.dragView addGestureRecognizer:panRecognizer];
[self updateExclusionZone];
}
-(void)move:(UIPanGestureRecognizer *)pan
{
[self.view bringSubviewToFront:[pan view]];
if ([pan state] == UIGestureRecognizerStateBegan) {
NSLog(#"pan began");
}
self.dragView.center = [pan locationInView:self.view];
[self updateExclusionZone];
if ([pan state] == UIGestureRecognizerStateEnded) {
NSLog(#"pan ended");
}
}
-(void)updateExclusionZone
{
CGRect dragViewFrame = self.dragView.frame;
CGRect exclusionRect = [self.view convertRect:dragViewFrame toView:self.textView];
UIBezierPath *exclusion = [UIBezierPath bezierPathWithRect:exclusionRect];
self.textView.textContainer.exclusionPaths = #[exclusion];
}
Any thoughts?
I ran into the same issue today.
This bug seem to appear, if you set editable and selectable at the same time. If only one or no is selected, it renders as expected. Both are selected by default.
If you need both options, just set them in code.
_textView.textContainer.exclusionPaths = exclusionPaths;
_textView.attributedText = attrString;
_textView.editable = YES;
_textView.selectable = YES;
I am using a custom view and there is a UIButton in it but it is not working. Of course the button is inside the custom view. It's driving me crazy. I tried by self.isUserInteractionEnabled = YES;, but getting an error "No setter method 'setIsUserInteractionEnabled:' for assignment". Can anyone help...
My header file...
#import <Foundation/Foundation.h>
#import "Clone.h"
#class UICloneInfoView;
#interface UICloneInfoView : UIImageView
{
Clone *clone;
}
#property(nonatomic, retain) Clone *clone;
#property(nonatomic, retain) UIButton *button;
#property(nonatomic, retain) UILabel *infoLabel;
// set a clone to show information on the bar
- (void)setClone:(Clone *)aClone;
#end
Here is the .m file...
#import "UICloneInfoView.h"
#define DEFAULT_HEIGHT_VIEW 90.0f
#define DEFAULT_WIDTH_VIEW 819.0f
#define DEFAULT_BUTTON_SIZE 40.0f
#implementation UICloneInfoView
#synthesize clone = _clone;
#synthesize button = _button;
#synthesize infoLabel = _infoLabel;
- (id)initWithImage:(UIImage *)image
{
self = [super initWithImage:image];
if (self) {
// init somthing
// init info label
_infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 5.0f, DEFAULT_WIDTH_VIEW - 80.0f, DEFAULT_HEIGHT_VIEW)];
_infoLabel.backgroundColor = [UIColor clearColor];
_infoLabel.font = [UIFont boldSystemFontOfSize:13.0f];
_infoLabel.lineBreakMode = UILineBreakModeCharacterWrap;
_infoLabel.numberOfLines = 3;
// init button
_button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_button.frame = CGRectMake(_infoLabel.frame.origin.x + _infoLabel.frame.size.width + 10.0f, (self.frame.size.height/2), DEFAULT_BUTTON_SIZE, DEFAULT_BUTTON_SIZE);
[_button setTitle:#"1" forState:UIControlStateNormal];
[_button addTarget:self action:#selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_button];
}
return self;
}
- (void)setClone:(Clone *)aClone
{
_clone = aClone;
// set label text
_infoLabel.text = [NSString stringWithFormat:#"Start Line: %d\tEnd Line: %d\t nLines: %d\tpcid: %d\nFile: %#", _clone.startLine, _clone.endLine, _clone.endLine - _clone.startLine + 1, _clone.pcid, _clone.sourcePath];
// adjust the label with new height
CGSize expectedLabelSize = [_infoLabel.text sizeWithFont:_infoLabel.font constrainedToSize:_infoLabel.frame.size lineBreakMode:_infoLabel.lineBreakMode];
CGRect newFrame = _infoLabel.frame;
newFrame.size.height = expectedLabelSize.height;
_infoLabel.frame = newFrame;
// add _infoLabel to view
[self addSubview:_infoLabel];
}
- (void)buttonAction:(id)sender
{
NSLog(#"%#", ((UIButton*)sender).titleLabel.text);
}
#end
Here is a snapshot
Thanks in advance.
UIImageView has userInteractionEnabled set to NO by default. You are adding the button as a subview to the image view. You should set it to YES.
It might be because your class is a subview of UIImageView, so the image view is handling all the touch events, and they are never reaching your button. You could play around with disabling user interaction on the image view so that the touch event gets to the button.
A far simpler solution would just be to make UICloneInfoView a subclass of UIView instead. Then you can add your button, your labels, Clone and then just add a UIImageView as a subview.
Is there any reason you wanted to subclass UIImageView? I don't know your requirements.
I'm a noob to iOS development (so please bear with me on this), and I'm trying to create a simple touch application with Xcode from scratch. I have spent a week on this, but I couldn't seem to find out what I have been missing so here I am, asking for some guidance :)
First, I created an empty application, then created a xib file (MainWindow.xib) and added a window object (Main_Window) to it. Then, I created a View object (Main_View) within this Main_Window, and added a label object (lblTitle) to this view. The Main_View object pretty much covered the entire Main_Window screen.
So, in short, the hierarchy of my MainWindow.xib is like this: Main_Window --> Main_View --> lblTitle.
Finally, I created a ViewController object (Main_View_Controller) with its "view" set to Main_View and its "rootViewController" set to "Main_Window".
In the project,
I subclassed UIView with "TouchEvent_View", hooked up to "Main_View" in the xib file.
I subclassed UIViewController with "TouchEvent_ViewController", hooked up to "Main_View_Controller" in the xib file.
In my AppDelegate.h,
I created an "IBOutlet UIWindow *window", hooked up to "Main_Window" in the xib file.
I created an object for my viewController and view classes.
#property (strong, nonatomic) IBOutlet UIWindow *window;
#property (strong, nonatomic) TouchEvent_ViewController * myViewController;
#property (strong, nonatomic) TouchEvent_View *myView;
In AppDelegate.m, I hooked up "MainWindow.xib" with myViewController:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.backgroundColor = [UIColor whiteColor];
self.myViewController = [[[TouchEvent_ViewController alloc]
initWithNibName:#"MainWindow" bundle:nil] autorelease];
[self.window makeKeyAndVisible];
return YES;
}
In "Touch_Event_ViewController.m", I coded the viewDidLoad message as followed:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Hi! ViewController's viewDidLoad msg sent!");
TouchEvent_View * mView = [[TouchEvent_View alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
mView.backgroundColor = [UIColor blueColor];
[self.view addSubview:mView];
[mView release];
}
In "TouchEvent_View.m", I instantiated a UITapGestureRecognizer object and hooked it up to a handler method as followed:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"[TouchEvent_View initWithFrame] sent!");
// Initialization code
//-----------------------
//Touch event declaration
//Single tap
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SingleTap_Handler)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
//singleTap.delegate = self;
[self addGestureRecognizer:singleTap];
}
return self;
}
-(void) SingleTap_Handler :(UITapGestureRecognizer *)GR
{
NSLog(#"Hi! You just touched the screen!");
}
When I compiled and deployed the project into my iPad3, every thing worked just as planned until the Touch event, which didn't work.
I got the following messages printed out to the console window:
2012-08-26 14:03:59.589 Ex_TouchEvents_01[806:707] Hi! ViewController's viewDidLoad msg sent!
2012-08-26 14:03:59.593 Ex_TouchEvents_01[806:707] [TouchEvent_View initWithFram] sent!
But I did not see the "Hi, you just touched the screen!" message after I touched the screen. In addition, I didn't see the background of the View area set to blue either. So, I must have been missing something that was very simple. I have been googling all over the web, but I couldn't figure out what I missed. Would some body kindly point out what I have done wrong?
I don't have access to my Mac to test this, but I believe you're missing a colon in this line.
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SingleTap_Handler)];
It needs to be:
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SingleTap_Handler:)];
If a method takes any parameters, the colon(s) are part of the selector.
Erm, can you try putting:
self.userinteractionEnabled = YES;
in your init method?
UIViews by default is set to not respond to touch events so you have to enabled them first before your tap gestures work.
I am having some problems with changing an image view inside of an MKAnnotationView subclass. I have a timer that updates the annotation position based on data about where a user was at a certain point. This works fine and the annotation position is updated correctly. I do this with the following code:
[_myAnnotation setCoordinate:point.position];
Below this, I also calculate whether the user was heading east or west. If the user is heading east, I call the setIsMovingRight:YES method on the MyAnnotation class, and do the opposite for when the user is heading west.
I have verified that the method is being called with the correct values at the correct times by using NSLog inside the method to print out whenever the value changes. However, it doesn't seem like the setIsMovingRight method has any effect on the visual appearance of the annotation. I try setting the background colour of the imageview to red whenever the method is called, but even this has no effect. I have tried calling setNeedsDisplay in various places with no success.
I can add and recreate an annotation view, but then the annotation flashes on and off whenever the position changes and it really does not look very good.
The following is the code for my custom annotation view, "MyAnnotation".
#interface MyAnnotation : MKAnnotationView <MKAnnotation>
{
UIImageView *_imageView;
BOOL _currentlyMovingRight;
}
#property (nonatomic) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic,retain) UIImage *image;
-(void)setIsMovingRight:(BOOL)movingRight;
#end
#implementation MyAnnotation
#synthesize coordinate, title, subtitle, image;
-(id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if(self)
{
_currentlyMovingRight = NO;
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"right.png"]];
[self addSubview:_imageView];
}
return self;
}
-(void)setIsMovingRight:(BOOL)movingRight
{
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[_imageView setBackgroundColor:[UIColor redColor]]; // THIS LINE NEVER DOES ANYTHING.
if(movingRight && !_currentlyMovingRight)
{
NSLog(#"right now.");
[_imageView setImage:[UIImage imageNamed:#"right.png"]];
}
else if (!movingRight && _currentlyMovingRight)
{
NSLog(#"left now.");
[_imageView setImage:[UIImage imageNamed:#"left.png"]];
}
_currentlyMovingRight = movingRight;
[self addSubview:_imageView];
[self setNeedsDisplay];
}
I would appreciate any help or advice that anyone could give. Thanks!
I think that the annotation views use KVO to listen for changes. Have you tried changing the image property? Something like [self setImage:myNewImage]