Screen freezes while scrolling UITableView - nsattributedstring

Screen freezes while scrolling UITableView if I am using attributedText for showing HTML text in a label inside UITableViewCell.
titleLbl.attributedText = [[NSAttributedString alloc] initWithData: [html dataUsingEncoding:NSUTF8StringEncoding]
options: #{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes: nil error: &err];
Can someone guide some best ways to do this...
I tried this link, but no help
NSAttributedString freeze UITableView.

Related

TitleView of UITableViewController's UINavigationItem image not visible

I am using Swift 2.3 in Xcode 7.3. I have a UINavigationController with a UITableViewController. I am trying to set the titleView of the navigation item in the navigationBar to create a button centered in the navigation bar.
The indexTextBut is a UIButton hooked up from the story board. I have tried just creating a new button and it made no perceivable difference. The way I am currently doing it the button is there as it has the behavior it should but no image. I have tried just setting the title field as a string and even then I see nothing. I set the right and left barButtonItems using:
navItem.setRightBarButtonItems(barButtonItems, animated: false)
navItemArray.append(navItemCatDet)
self.navigationController?.navigationBar.setItems(navItemArray, animated: false)
Here is my relevant code. My big question is why can't I see the image of the button being set to the titleView. It has preset behavior and when I click where it should be I get that behavior. I've tried adjusting the frame of the UIButton and its UIImageView, and the size of the UIImageView's image but have had no luck. I included the line aobut tinting the background just incase it is related even though I have tried it with and without.
class MyTableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var indexTextBut: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
var indexNavItemArray = [UINavigationItem]()
var indexNavItem = UINavigationItem()
indexNavItem.titleView = newButton
indexNavItemArray.append(indexNavItem)
self.navigationController?.navigationBar.barTintColor = UIColor(netHex:0xe63246)
self.navigationController?.navigationBar.setItems(indexNavItemArray, animated: false)
swift 3.0
var indexNavItemArray = [UINavigationItem]()
var indexNavItem = UINavigationItem()
indexNavItem.titleView = indexTextBut
indexTextBut.setTitle("button", for: .normal)
indexNavItemArray.append(indexNavItem)
self.navigationController?.navigationBar.barTintColor = UIColor.lightGray
self.navigationController?.navigationBar.setItems(indexNavItemArray, animated: false)
replace your code to above.

How do I scroll an Editor that has been obscured by the keyboard into view?

I'm designing a UI in Xamarin.Forms to collect feedback from users about our application. There is an Editor control at the bottom of this page. On an iPhone 4S, and in many landscape orientations, the keyboard completely obscures this editor control. On Android, this is not a big deal because the OS automatically scrolls (though the sizing behavior is a little weird.) On iOS, the only things resembling solutions are very wonky.
In native iOS, the solution is simple: wrap your views in a UIScrollView, then when the keyboard appears add that much space to the content size and scroll appropriately. Xamarin doesn't expose anything to control the scroll position in ScrollView, and ContentSize is private, so that's out. A few posts (here and here) seem to indicate ScrollView is at least part of the solution. It does appear Xamarin has some automatic scrolling behavior, but it's... curious.
My layout is fairly simple:
At the top, a fixed navigation bar that I do not want to scroll out of view.
Beneath that, a 180px tall image that represents a screenshot of the application.
Beneath that, a label with information such as the timestamp. (2-3 lines of text).
Beneath that, the editor, filling the remaining available space.
I've included code for a layout I've tried at the bottom of my post. I created a StackLayout that contains the image, the label, and the editor. I put that inside a ScrollView. Then, I create a RelativeLayout and place the navigation bar at the top-left with the ScrollView beneath it.
What I want to happen when the Editor is tapped is for the keyboard to be displayed and, if it obscures the Editor, for the layout to be nudged upwards to make the Editor visible. What happens instead is it seems like Xamarin scrolls the layout upwards by the keyboard height plus some margin that looks suspiciously like the keyboard utility bar height. This shoves the Editor upwards so high it's obscured by the navigation bar.
I've tried a lot of different tweaks and I'm at a loss. I can't control enough of the ScrollView to get the behavior I need. I've seen suggestions that use a BoxView resized when the Editor gains focus, but to make it work really well I'd still have to hook into the iOS notifications to get the appropriate size and have a fairly intimate knowledge of where my Editor's bounds are. It feels wrong.
Does anyone else have a solution to this on Xamarin.Forms? Even if I have to dip into native, I'd like an answer.
(Here's an example layout that demonstrates the problem, there's a little bit of weird structure because I was debugging. The funky colors are also a relic of layout debugging.)
using System;
using Xamarin.Forms;
namespace TestScroll
{
public class MainPage : ContentPage {
public MainPage() {
InitializeComponent();
}
private ScrollView _scroller;
protected void InitializeComponent() {
var mainLayout = new RelativeLayout();
var navbar = new Label() {
BackgroundColor = Color.Blue,
TextColor = Color.White,
Text = "I am the Nav Bar",
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.StartAndExpand
};
var subLayout = new ScrollView() {
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand
};
_scroller = subLayout;
var subStack = new StackLayout();
subStack.Spacing = 0;
subLayout.Content = subStack;
var image = new BoxView() {
Color = Color.Green,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.Fill,
HeightRequest = 300
};
subStack.Children.Add(image);
var infoLabel = new Label() {
BackgroundColor = Color.Blue,
TextColor = Color.Black,
Text = "Timestamp!\r\nOther stuff!",
VerticalOptions = LayoutOptions.Start
};
subStack.Children.Add(infoLabel);
var editor = new Editor() {
VerticalOptions = LayoutOptions.FillAndExpand
};
subStack.Children.Add(editor);
mainLayout.Children.Add(navbar,
Constraint.Constant(0),
Constraint.Constant(20),
Constraint.RelativeToParent((parent) => parent.Width),
Constraint.Constant(70));
mainLayout.Children.Add(subLayout,
Constraint.Constant(0),
Constraint.RelativeToView(navbar, (parent, view) => navbar.Bounds.Bottom),
Constraint.RelativeToParent((parent) => parent.Width),
Constraint.RelativeToView(navbar, TestConstraint));
Content = mainLayout;
}
private double TestConstraint(RelativeLayout parent, View view) {
double result = parent.Height - view.Bounds.Height;
Console.WriteLine ("Lower stack height : {0}", result);
Console.WriteLine ("Scroll content size: {0}", _scroller.ContentSize);
return result;
}
}
}
One thing I notice is that you are adding a ScrollView (subLayout) to another ScrollView (_scroller).
Also, I ran into this same problem on iOS except all of my controls were within a Grid. Simply putting the Grid into a single ScrollView fixed the problem, without having to change content sizes or anything like that.
This question sat for a long time unanswered, here's what I did. I don't know that it's the 'answer', and I do appreciate hvaughan3's answer that's currently here and I will try it if I ever get the time.
My page behaved like I wanted on Android, so I didn't do anything specific for that.
So I wrote specific code for iOS that used the notifications UIKeyboardWillShow and UIKeyboardWillHide. These notifications provide information about the bounds the keyboard will take up. So when I get a 'show' notification, I manipulate my layout to allow room for an element of that size I place underneath the keyboard. When I get a 'hide' notification, I reset the layout.
It's janky and a little embarrassing, I hope to come back with news I tried another solution like hvaughan3's and it worked.

UIView not resizing when rotated with a CGAffineTransform under iOS8

I have a UIViewController that only rotates some of it subviews when the device is rotated. This works fine under iOS7 but breaks under iOS8. It appears that the UIView's bounds are adjusted by the transform under iOS8. This was unexpected.
Here's some code:
#interface VVViewController ()
#property (weak, nonatomic) IBOutlet UIView *pinnedControls;
#property (nonatomic, strong) NSMutableArray *pinnedViews;
#end
#implementation VVViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.pinnedViews = [NSMutableArray array];
[self.pinnedViews addObject:self.pinnedControls];
}
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[UIViewController rotatePinnedViews:self.pinnedViews forOrientation:self.interfaceOrientation];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
[UIViewController rotatePinnedViews:self.pinnedViews forOrientation:toInterfaceOrientation];
}
}
#end
We've made a category on UIViewController to handle this behavior. Here's the pertinent code:
#implementation UIViewController (VVSupport)
+ (void)rotatePinnedViews:(NSArray *)views forOrientation:(UIInterfaceOrientation)orientation {
const CGAffineTransform t1 = [UIViewController pinnedViewTansformForOrientation:orientation counter:YES];
const CGAffineTransform t2 = [UIViewController pinnedViewTansformForOrientation:orientation counter:NO];
[views enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
// Rotate the view controller
view.transform = t1;
[view.subviews enumerateObjectsUsingBlock:^(UIView *counterView, NSUInteger idx, BOOL *stop) {
// Counter-rotate the controlsUIin the view controller
counterView.transform = t2;
}];
}];
}
+ (CGAffineTransform)pinnedViewTansformForOrientation:(UIInterfaceOrientation)orientation counter:(BOOL)counter {
CGAffineTransform t;
switch ( orientation ) {
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
t = CGAffineTransformIdentity;
break;
case UIInterfaceOrientationLandscapeLeft:
t = CGAffineTransformMakeRotation(counter ? M_PI_2 : -M_PI_2);
break;
case UIInterfaceOrientationLandscapeRight:
t = CGAffineTransformMakeRotation(counter ? -M_PI_2 : M_PI_2);
break;
}
return t;
}
#end
Here's what the nib looks like:
The UIView named pinned in the nib is the IBOutlet of pinnedControls:
When I run this in portrait mode under iOS7 or iOS8 I get this:
And I see the desired outcome under iOS7 in landscape mode:
But under iOS8 (GM) I do not get this behavior. This is what I see instead:
Notice that the center of the UILabel with the text "Pinned Label" is maintaining its distance from the bottom of the pinned UIView, which has not changed size to accommodate the rotation. That UIView has all its edges pinned to the top, left, bottom and right sides of the super view.
It looks to me that the transform property interacts with Auto Layout differently under iOS8. I'm a bit baffled here. I know I can't rely on the frame. I may just start manually setting bounds but that just seems like the wrong thing to do, essentially do an end run around Auto Layout.
So this was driving me crazy for the past couple days and I was able to fix by changing the timing of the setTransform call in my animations block
When going to landscape, I'm setting the transform AFTER setting up the new frame. When going portrait, I'm setting the transform BEFORE setting up the new frame. All this was going inside the animations block on the "animateWithDuration..." method
I'm not sure if it will help you directly with your code, but it might spark some inspiration to solve it since we are definitely having a similar issue
This is more of a work around than a fix so it may not help everybody in similar situations. My problem was that the outer "pinned" view was being resized again after the transform was applied.
My solution was to change the constraints on the pinned view to be center vertically, center horizontally, and width and height equal a constant.
Then, in viewDidLoad, I set the height and width of the pinned view's frame to be the height of the main screen. This makes the view square so I don't care if it gets an extra rotate.
in ios8, uiviewcontrollers need to be resized with uitraitcollections depending on the device orientation. Otherwise, you get a uiview in portrait mode, while the phone oriented in landscape, when you try to rotate it. So the correct steps are to rotate AND override uitraitcollections
EDIT:
I override my uitraitcollection with the following code
UITraitCollection *horTrait = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
UITraitCollection *verTrait = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
UITraitCollection *finalTrait = [UITraitCollection traitCollectionWithTraitsFromCollections:#[horTrait,verTrait]];
[self.parentViewController setOverrideTraitCollection:finalTrait forChildViewController:self];
unfortunately, it doesnt work if the uiviewcontroller im trying to modify does NOT have a parentviewcontroller :'(

NSRulerView overtop of NSTableView?

Very basic scenario:
I have a NSTableView in my nib with an outlet pointing to it. I have the following in my app delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSScrollView *scroller = [tableView enclosingScrollView];
[scroller setHasVerticalRuler: YES];
[scroller setRulersVisible: YES];
}
The ruler gets displayed overtop of my tables headers (any any content):
See how column 1 is only partially visible. If I try the same exact thing with a textview, then it works perfect, the cursor gets indented and the text is clearly visible:
Could anyone point out what I'm doing wrong?
Edit: I've discovered that this is an issue with Yosemite that did not exist on older OS X versions. I've submitted a bug reports, but I guess the question would now be, does anybody know of a workaround?
A sample project has been uploaded here.
The problem is in the NSScrollView, and it's not your fault. Basically, the reason why column1 is partially visible is because the horizontal scroller has an offset.
Anyway, this worked for me:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSScrollView *scroller = [tableView enclosingScrollView];
[scroller setHasVerticalRuler: YES];
[scroller setRulersVisible: YES];
NSPoint pointToScrollTo = NSMakePoint ( -25 , 0 );
[[scroller contentView] scrollToPoint: pointToScrollTo];
[scroller reflectScrolledClipView: [scroller contentView]];
}
Hope this helps.

After Taking Picture cannot select Use Photo or Retake

So I'm trying to update an app for iOS 7 and I'm running into issues with my custom overlay. The overlay is an image that I'm framing the photo with (both live and using a full resolution version to frame the final result in the camera roll). The problem is that now, under iOS 7, the overlay, while transparent at the bottom, provides access to the regular "take picture" button, but for some reason will not let me tap on the "Use Photo" or "Retake" buttons that come up after the picture is snapped. Here's the code snippet calling the view controller:
- (IBAction)takePhoto:(UIButton *)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.showsCameraControls = YES;
// Overlay Creation
UIView* overlayView = [[UIView alloc] initWithFrame:picker.view.frame];
overlayView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"PBOverlayView.png"]];
[overlayView.layer setOpaque:NO];
overlayView.opaque = NO;
picker.cameraOverlayView = overlayView;
[self presentViewController:picker animated:YES completion:NULL];
}
Another approach could be to observe the notifications when the ImagePicker changes state and remove (or disable) your overlay when you move into the "Use Photo" screen.
- (void) addPhotoObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(removeCameraOverlay) name:#"_UIImagePickerControllerUserDidCaptureItem" object:nil ];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(addCameraOverlay) name:#"_UIImagePickerControllerUserDidRejectItem" object:nil ];
}
- (void) removePhotoObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)addCameraOverlay {
if (self.cameraPicker) {
self.cameraPicker.cameraOverlayView = self.myCameraOverlayView;
}
}
-(void)removeCameraOverlay {
if (self.cameraPicker) {
self.cameraPicker.cameraOverlayView = nil;
}
}
You could set User Enteraction Enabled to NO on Overlay View ;) works for me
Your problem: When you initialised the overlayView, you've set the frame to be the same size as that of the picker.
UIView* overlayView = [[UIView alloc] initWithFrame:picker.view.frame];
Explanation: Before capturing an image, the camera buttons are in the foreground of the overlayView, so that there's no issue pressing them.
After capturing an image (on the "retake/use preview page"), the overlayView is the one to be in the foreground, thus blocking the access to the buttons.
I know this to be an issue on iOS7, and not sure about other versions.
Possible solutions: Since this is a native problem within Apple's UIImagePickerController, I can only think of two solutions: (1) If possible, configure the overlayView to have a shorter frame which doesn't cover the bottom of the picker; (2) If you need the overlay to cover these buttons, you still have the exhausting option of configuring self.imagePicker.showsCameraControls = NO; but then you'd have to customize ALL of the camera behaviour (you can find many examples for that on the web).
After taking a photo, or animation on your overlay is done, i made it work by removing the overlay from its superview.
[UIView animateWithDuration:1
delay:1
options:UIViewAnimationOptionCurveEaseOut
animations:^{
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
}];

Resources