I have a UITextField that I want to enlarge its width when tapped on. I set up the constraints and made sure the constraint on the left has the lower priority then the one that I am trying to animate on the right side.
Here is the code that I am trying to use.
// move the input box
UIView.animateWithDuration(10.5, animations: {
self.nameInputConstraint.constant = 8
}, completion: {
(value: Bool) in
println(">>> move const")
})
This works, but it seems to just happen instantly and there doesn't seem to be any movement. I tried to set it 10 seconds to make sure I wasn't missing anything, but I got the same results.
nameInputConstraint is the name of the constraint that I control dragged to connect into my class from IB.
You need to first change the constraint and then animate the update.
This should be in the superview.
self.nameInputConstraint.constant = 8
Swift 2
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}
Swift 3, 4, 5
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
SWIFT 4 and above:
self.mConstraint.constant = 100.0
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
Example with completion:
self.mConstraint.constant = 100
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
}, completion: {res in
//Do something
})
It's very important to point out that view.layoutIfNeeded() applies to the view subviews only.
Therefore to animate the view constraint, it is important to call it on the view-to-animate superview as follows:
topConstraint.constant = heightShift
UIView.animate(withDuration: 0.3) {
// request layout on the *superview*
self.view.superview?.layoutIfNeeded()
}
An example for a simple layout as follows:
class MyClass {
/// Container view
let container = UIView()
/// View attached to container
let view = UIView()
/// Top constraint to animate
var topConstraint = NSLayoutConstraint()
/// Create the UI hierarchy and constraints
func createUI() {
container.addSubview(view)
// Create the top constraint
topConstraint = view.topAnchor.constraint(equalTo: container.topAnchor, constant: 0)
view.translatesAutoresizingMaskIntoConstraints = false
// Activate constaint(s)
NSLayoutConstraint.activate([
topConstraint,
])
}
/// Update view constraint with animation
func updateConstraint(heightShift: CGFloat) {
topConstraint.constant = heightShift
UIView.animate(withDuration: 0.3) {
// request layout on the *superview*
self.view.superview?.layoutIfNeeded()
}
}
}
With Swift 5 and iOS 12.3, according to your needs, you may choose one of the 3 following ways in order to solve your problem.
#1. Using UIView's animate(withDuration:animations:) class method
animate(withDuration:animations:) has the following declaration:
Animate changes to one or more views using the specified duration.
class func animate(withDuration duration: TimeInterval, animations: #escaping () -> Void)
The Playground code below shows a possible implementation of animate(withDuration:animations:) in order to animate an Auto Layout constraint's constant change.
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let textView = UITextView()
lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)
override func viewDidLoad() {
view.backgroundColor = .white
view.addSubview(textView)
textView.backgroundColor = .orange
textView.isEditable = false
textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
textView.translatesAutoresizingMaskIntoConstraints = false
textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
heightConstraint.isActive = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
textView.addGestureRecognizer(tapGesture)
}
#objc func doIt(_ sender: UITapGestureRecognizer) {
heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
UIView.animate(withDuration: 2) {
self.view.layoutIfNeeded()
}
}
}
PlaygroundPage.current.liveView = ViewController()
#2. Using UIViewPropertyAnimator's init(duration:curve:animations:) initialiser and startAnimation() method
init(duration:curve:animations:) has the following declaration:
Initializes the animator with a built-in UIKit timing curve.
convenience init(duration: TimeInterval, curve: UIViewAnimationCurve, animations: (() -> Void)? = nil)
The Playground code below shows a possible implementation of init(duration:curve:animations:) and startAnimation() in order to animate an Auto Layout constraint's constant change.
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let textView = UITextView()
lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)
override func viewDidLoad() {
view.backgroundColor = .white
view.addSubview(textView)
textView.backgroundColor = .orange
textView.isEditable = false
textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
textView.translatesAutoresizingMaskIntoConstraints = false
textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
heightConstraint.isActive = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
textView.addGestureRecognizer(tapGesture)
}
#objc func doIt(_ sender: UITapGestureRecognizer) {
heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
let animator = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: {
self.view.layoutIfNeeded()
})
animator.startAnimation()
}
}
PlaygroundPage.current.liveView = ViewController()
#3. Using UIViewPropertyAnimator's runningPropertyAnimator(withDuration:delay:options:animations:completion:) class method
runningPropertyAnimator(withDuration:delay:options:animations:completion:) has the following declaration:
Creates and returns an animator object that begins running its animations immediately.
class func runningPropertyAnimator(withDuration duration: TimeInterval, delay: TimeInterval, options: UIViewAnimationOptions = [], animations: #escaping () -> Void, completion: ((UIViewAnimatingPosition) -> Void)? = nil) -> Self
The Playground code below shows a possible implementation of runningPropertyAnimator(withDuration:delay:options:animations:completion:) in order to animate an Auto Layout constraint's constant change.
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let textView = UITextView()
lazy var heightConstraint = textView.heightAnchor.constraint(equalToConstant: 50)
override func viewDidLoad() {
view.backgroundColor = .white
view.addSubview(textView)
textView.backgroundColor = .orange
textView.isEditable = false
textView.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
textView.translatesAutoresizingMaskIntoConstraints = false
textView.topAnchor.constraint(equalToSystemSpacingBelow: view.layoutMarginsGuide.topAnchor, multiplier: 1).isActive = true
textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
heightConstraint.isActive = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(doIt(_:)))
textView.addGestureRecognizer(tapGesture)
}
#objc func doIt(_ sender: UITapGestureRecognizer) {
heightConstraint.constant = heightConstraint.constant == 50 ? 150 : 50
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 0, options: [], animations: {
self.view.layoutIfNeeded()
})
}
}
PlaygroundPage.current.liveView = ViewController()
In my case, I only updated the custom view.
// DO NOT LIKE THIS
customView.layoutIfNeeded() // Change to view.layoutIfNeeded()
UIView.animate(withDuration: 0.5) {
customViewConstraint.constant = 100.0
customView.layoutIfNeeded() // Change to view.layoutIfNeeded()
}
I would like to share my solution, in my not working case my constraint designed like below
view1WidthLayout = NSLayoutConstraint(item: view1,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .width,
multiplier: 1,
constant: 20)
and whenever I tried to set constant before the animation like
view1WidthLayout.constant += 20
it set immediately so it did not work for me.
I change the definition of the constraint property like
view1WidthLayout = view1.widthAnchor.constraint(equalToConstant: 20)
then it worked for myself
Watch this.
The video says that you need to just add self.view.layoutIfNeeded() like the following:
UIView.animate(withDuration: 1.0, animations: {
self.centerX.constant -= 75
self.view.layoutIfNeeded()
}, completion: nil)
Related
I created a swift file using a class called Course . Everything works in my code except when I try to input the URL. I get an error Message.
Here's is my sample code
URL(String:"https://www.youtube.com/watch?v=QBULK-w-i90")!
I keep getting error
Argument labels '(String)' do not match any available overloads
I'm not sure what that means and how can I fix this problem .
here is the code written out I follow video but keep getting errors.
import UIKit
class CourseStore
{
class func downloadNewCourses()-> [Course]
{
var courses = [Course]()
let chefCourse = Course(buttonImage: #imageLiteral(resourceName: "cover_chef"),
title: "Become a Pro Chef",
instructor: "Gordon Ramsay",
featuredImage: #imageLiteral(resourceName: "chef"),
introductionVideoUrl: URL(String:"https://www.youtube.com/watch?v=QBULK-w-i90")!,
introductionVideoUrl: ,
description: "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."
)
return courses
}
}
Argument labels '(String)' do not match any available overloads
Means that URL doesn't have constructor which has those parameters. In your case you named parameter String but it should be lowercased string.
Instead use
URL(string: "https://www.youtube.com/watch?v=QBULK-w-i90")!
I have the following file format:
Text1
+ continuation of Text1
+ more continuation of Text1
Text2
+ continuation of Text2
+ more continuation of Text2
+ even more continuation of Text2
Continuations are marked by \n+. (Newline, plus character, space as a three character string.) Continuations can be any number of lines, including 0.
I want the following output (each is a line printed with .forEach):
Text1 continuation of Text1 more continuation of Text1
Text2 continuation of Text2 more continuation of Text2 even more continuation of Text2
I would like to use only Java streams to do the conversion, preferably with Collect. Is there a way to do this elegantly?
EDIT:
Another, more realistic example:
Lorem ipsum dolor sit amet, consectetur
+ adipiscing elit, sed do eiusmod tempor incididunt
+ ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex
+ ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
+ esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+ occaecat cupidatat non proident, sunt in culpa qui officia
+ deserunt mollit anim id est laborum.
Expected result is two lines:
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
In Java 9, you could use
static final Pattern LINE_WITH_CONTINUATION = Pattern.compile("(\\V|\\R\\+)+");
…
try(Scanner s = new Scanner(file)) {
s.findAll(LINE_WITH_CONTINUATION)
.map(m -> m.group().replaceAll("\\R\\+", ""))
.forEach(System.out::println);
}
Since Java 8 lacks the Scanner.findAll(Pattern) method, you may add a custom implementation of the operation as a work-around
public static Stream<MatchResult> findAll(Scanner s, Pattern pattern) {
return StreamSupport.stream(new Spliterators.AbstractSpliterator<MatchResult>(
1000, Spliterator.ORDERED|Spliterator.NONNULL) {
public boolean tryAdvance(Consumer<? super MatchResult> action) {
if(s.findWithinHorizon(pattern, 0)!=null) {
action.accept(s.match());
return true;
}
else return false;
}
}, false);
}
which can be used like
try(Scanner s = new Scanner(file)) {
findAll(s, LINE_WITH_CONTINUATION)
.map(m -> m.group().replaceAll("\\R\\+", ""))
.forEach(System.out::println);
}
which will make the future migration easy.
Assuming that you run this sequentially only and really want to use streams:
List<String> result = Files.lines(Paths.get("YourPath"))
.collect(() -> new ArrayList<>(), (list, line) -> {
int listSize = list.size();
if (line.startsWith("+ ")) {
list.set(listSize - 1, list.get(listSize - 1) + line.substring(2));
} else {
list.add(line);
}
}, (left, right) -> {
throw new RuntimeException("Not for parallel processing");
});
If I create a label in interface builder and set a string through code that does not fit its current size, the label will grow vertically to fit its size, great!.
Besides the x & y constraint, Xcode creates a NSContentSizeLayoutConstraint that contains hugging and compression resistance.
Here is a log output of the constraint
<NSContentSizeLayoutConstraint:0x6080000a11a0 V:[NSTextField:0x600000180c30(17)] Hug:750 CompressionResistance:750>
The question is, how to do this programmatically? I have not found a way yet. There is no way to create a NSContentSizeLayoutConstraint and setting the contentCompressionResistance of the NSTextField does not seem to have any effect.
Here is my code:
self.label = [[NSTextField alloc]initWithFrame:NSMakeRect(0, 0, 100, 20)];
[self.label setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.label setBordered:YES];
[[self.label cell] setLineBreakMode:NSLineBreakByWordWrapping];
[self.window.contentView addSubview:self.label];
self.label.stringValue = #"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
NSDictionary *dict = #{#"label" : self.label};
[self.label.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[label]"options:0 metrics:nil views:dict]];
[self.label.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[label]" options:0 metrics:nil views:dict]];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:0 constant:40];
constraint.priority = 1;
[self.label addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:0 constant:18];
constraint.priority = 1;
[self.label addConstraint:constraint];
[self.label setContentCompressionResistancePriority:1000 forOrientation:NSLayoutConstraintOrientationHorizontal];
[self.label setContentCompressionResistancePriority:1000 forOrientation:NSLayoutConstraintOrientationVertical];
Set the text field's preferredMaxLayoutWidth. I think you then don't want or need the explicit width constraint.
In my testing, I find that this works:
self.label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 18)];
self.label.translatesAutoresizingMaskIntoConstraints = NO;
[self.label.cell setLineBreakMode:NSLineBreakByWordWrapping];
self.label.stringValue = #"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
self.label.preferredMaxLayoutWidth = 100;
self.label.editable = NO;
[self.window.contentView addSubview:self.label];
NSDictionary* views = #{ #"label" : self.label };
[self.label.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[label]" options:0 metrics:nil views:views]];
[self.label.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[label]" options:0 metrics:nil views:views]];
Basically, the main difference from yours is that I set editable to NO. I also leave out the width and height constraints and setting the compression resistance.
I think a better way to do this, is to override NSTextField, and update the intrinsicContentSize.
public class DynamicTextField: NSTextField {
public override var intrinsicContentSize: NSSize {
if cell!.wraps {
let fictionalBounds = NSRect(x: bounds.minX, y: bounds.minY, width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
return cell!.cellSize(forBounds: fictionalBounds)
} else {
return super.intrinsicContentSize
}
}
public override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
if cell!.wraps {
validateEditing()
invalidateIntrinsicContentSize()
}
}
}
Afterwards the constraints works as expected. Note that it only works when you set the Layout to Wraps in Interface Builder or code.
Trying to teach myself how to do custom drawing in OS X. I am trying to nest a NSTextView within a NSView.
I can't seem to figure out the step I am missing to get the NSTextView to behave as if it wasn't embedded in another custom view (I.e., text should begin redering from top-left in the frame provided to the NSTextView, left-to-right and top-to-bottom).
import Cocoa
import AppKit
import XCPlayground
class MyView : NSView {
let lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
override init(frame: NSRect) {
super.init(frame:frame)
self.wantsLayer = true
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
let grey = NSColor.grayColor()
self.layer?.backgroundColor = grey.CGColor
let boundaryRect = NSInsetRect(dirtyRect, 10, 10)
let textRect = NSInsetRect(boundaryRect, 10, 10)
let path = NSBezierPath(roundedRect: boundaryRect, xRadius: 5, yRadius: 5)
path.stroke()
let text = NSTextView(frame: textRect)
text.backgroundColor = grey
text.insertText(lipsum)
text.drawRect(textRect)
}
}
var frame = NSRect(x: 0, y: 0, width: 400, height: 400)
let myView = MyView(frame: frame)
let textView = NSTextView(frame:frame)
textView.insertText(myView.lipsum)
XCPShowView("my view", myView)
XCPShowView("text view", textView)
You haven't actually embedded the text view inside the parent view. You can do that with addSubview().
let text = NSTextView(frame: textRect)
text.backgroundColor = grey
text.insertText(lipsum)
self.addSubview(text) // <---------
}
I'm trying to implement text search with linq. I have a Messages table which is populated with emails data. I want to be able to search in Messages body. However emails bodies are very long and I will like to display only small part of the searched text for example if I search for:
aute
in the following text:
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
the result should be:
...aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur....
Thanks
Something like this should do the job:
var query = from str in messages
let index = str.IndexOf(search)
where index > -1
select str.Substring(Math.Max(0, index - radius), radius + Math.Min(radius, str.Length - index));
Where messages is your email string list, and radius is an int describing how many characters you want to take before and after the string you are looking for. Note that this code will only return the first match in every email, ignoring the other ones.
Everything would be easier if you could use an helper function to calculate the proper substring tho.
Here you can find an extension method that implements a safe version of string.substring, making hte above linq code look something like:
var query = from str in lst
let index = str.IndexOf(search)
where index > -1
select str.SafeSubstring(index - radius, 2*radius);
that in my opinion is much simnpler to read
EDIT
Extending string with the two following methods :
public static List<int> IndexOfAll(this String str, string search)
{
List<int> lst = new List<int>();
foreach (Match match in Regex.Matches(str,search))
{
lst.Add(match.Index);
}
return lst;
}
public static string SafeSubstring(this String str, int start, int n)
{
return str.Substring(Math.Max(start, 0), Math.Min(n, str.Length - start));
}
you can get all the results in a nice form, using
var query = from str in lst
let index = str.IndexOfAll(search)
where index.Count>0
select index.Select(x => str.SafeSubstring(x-radius, 2*radius));
with query as an IEnumerable