Мне кажется, что для того, чтобы быть эффективным, вам нужно: 1) создать случайное число от 0 до единицы меньше, чем количество записей в базе данных, и 2) иметь возможность выбирать строку в этой позиции. К сожалению, разные базы данных имеют разные генераторы случайных чисел и различные способы выбора строки в позиции в наборе результатов - обычно вы указываете, сколько строк нужно пропустить и сколько строк вы хотите, но для разных баз данных это делается по-разному. Вот что работает для меня в SQLite:
select *
from Table
limit abs(random()) % (select count(*) from Words), 1;
Это зависит от возможности использования подзапроса в предложении limit (который в SQLite является LIMIT & lt; recs для пропуска & gt;, & lt; recs взять & gt;) Выбор количества записей в таблице должен быть особенно эффективным, являясь частью метаданных базы данных, но это зависит от реализации базы данных. Кроме того, я не знаю, будет ли запрос фактически создавать набор результатов до получения N-й записи, но я надеюсь, что это не понадобится. Обратите внимание, что я не указываю предложение «order by». Возможно, лучше «упорядочить» что-то вроде первичного ключа, который будет иметь индекс - получение N-й записи из индекса может быть более быстрым, если база данных не сможет получить N-й запись из самой базы данных, не создавая набор результатов .
Раздел Layer Geometry and Transforms в Руководстве по программированию основной анимации объясняет взаимосвязь между позицией CALayer и свойствами anchorPoint. В принципе, положение слоя определяется с точки зрения местоположения anchorPoint слоя. По умолчанию, anchorPoint слоя (0.5, 0.5), который лежит в центре слоя. Когда вы устанавливаете положение слоя, вы затем устанавливаете расположение центра слоя в его системе координат суперслоя.
Поскольку позиция относительно привязанного уровня слоя, изменение этого anchorPoint во время поддержание той же позиции перемещает слой. Чтобы предотвратить это движение, вам нужно будет отрегулировать позицию слоя для учета нового anchorPoint. Один из способов, которым я это сделал, - захватить границы слоя, умножить ширину и высоту границ на старые и новые нормальные значения anchorPoint, различить два якорных точки и применить эту разницу к положению слоя.
Возможно, вы даже сможете учесть ротацию таким образом, используя CGPointApplyAffineTransform()
с CGAffineTransform вашего UIView.
Для меня понимание position
и anchorPoint
было проще всего, когда я начал сравнивать его с моим пониманием frame.origin в UIView. UIView с frame.origin = (20,30) означает, что UIView составляет 20 точек слева и 30 точек от верхней части его родительского вида. Это расстояние рассчитывается из какой точки UIView? Его вычисляли из верхнего левого угла UIView.
В слое anchorPoint
отмечается точка (в нормализованной форме, то есть от 0 до 1), от которой это расстояние вычисляется так, например, layer.position = (20, 30) означает, что слой anchorPoint
равен 20 точкам слева и 30 точкам от вершины его родительского слоя. По умолчанию слой anchorPoint равен (0,5, 0,5), поэтому точка вычисления расстояния находится прямо в центре слоя. Следующий пример поможет пояснить мою точку зрения:
[/g0]
anchorPoint
также оказывается точкой, вокруг которой будет происходить поворот в случае применения преобразования к слою.
Для Swift 3:
func setAnchorPoint(_ anchorPoint: CGPoint, forView view: UIView) {
var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x, y: view.bounds.size.height * anchorPoint.y)
var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x, y: view.bounds.size.height * view.layer.anchorPoint.y)
newPoint = newPoint.applying(view.transform)
oldPoint = oldPoint.applying(view.transform)
var position = view.layer.position
position.x -= oldPoint.x
position.x += newPoint.x
position.y -= oldPoint.y
position.y += newPoint.y
view.layer.position = position
view.layer.anchorPoint = anchorPoint
}
Расширение на Magnus 'great & amp; полный ответ, я создал версию, которая работает на подслоях:
-(void)setAnchorPoint:(CGPoint)anchorPoint forLayer:(CALayer *)layer
{
CGPoint newPoint = CGPointMake(layer.bounds.size.width * anchorPoint.x, layer.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(layer.bounds.size.width * layer.anchorPoint.x, layer.bounds.size.height * layer.anchorPoint.y);
CGPoint position = layer.position;
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
layer.position = position;
layer.anchorPoint = anchorPoint;
}
Для тех, кому это нужно, вот решение Магнуса в Swift:
func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
var newPoint: CGPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
var oldPoint: CGPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)
newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)
var position: CGPoint = view.layer.position
position.x -= oldPoint.x
position.x += newPoint.x
position.y -= oldPoint.y
position.y += newPoint.y
view.setTranslatesAutoresizingMaskIntoConstraints(true) // Added to deal with auto layout constraints
view.layer.anchorPoint = anchorPoint
view.layer.position = position
}
view.setTranslatesAutoresizingMaskIntoConstraints(true)
. Спасибо чувак
– Oscar Yuandinata
22 March 2018 в 04:09
view.setTranslatesAutoresizingMaskIntoConstraints(true)
необходим при использовании ограничений автоматического макета.
– SamirChen
8 June 2018 в 09:56
Ключом к решению этого вопроса было использование свойства frame, которое, как ни странно, меняется только.
Swift 2
let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(1, 1)
self.frame = oldFrame
Swift 3
let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 1, y: 1)
self.frame = oldFrame
Затем я делаю свой размер, где он масштабируется из anchorPoint. Затем мне нужно восстановить старый anchorPoint;
Swift 2
let oldFrame = self.frame
self.layer.anchorPoint = CGPointMake(0.5,0.5)
self.frame = oldFrame
Swift 3
let oldFrame = self.frame
self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.frame = oldFrame
РЕДАКТИРОВАТЬ: это сбрасывается, если просмотр повернут , поскольку свойство кадра не определено, если применяется CGAffineTransform.
Если вы измените anchorPoint, его положение также изменится, ЕСЛИ вы не указали нулевую точку CGPointZero
.
position.x == origin.x + anchorPoint.x;
position.y == origin.y + anchorPoint.y;
Это альтернативное решение, которое позволяет изменить точку привязки через инспектора атрибутов и имеет еще одно свойство, чтобы просмотреть точку привязки для подтверждение.
import UIKit
@IBDesignable
class UIViewAnchorPoint: UIView {
@IBInspectable var showAnchorPoint: Bool = false
@IBInspectable var anchorPoint: CGPoint = CGPoint(x: 0.5, y: 0.5) {
didSet {
setAnchorPoint(anchorPoint: anchorPoint)
}
}
override func draw(_ rect: CGRect) {
if showAnchorPoint {
let anchorPointlayer = CALayer()
anchorPointlayer.backgroundColor = UIColor.red.cgColor
anchorPointlayer.bounds = CGRect(x: 0, y: 0, width: 6, height: 6)
anchorPointlayer.cornerRadius = 3
let anchor = layer.anchorPoint
let size = layer.bounds.size
anchorPointlayer.position = CGPoint(x: anchor.x * size.width, y: anchor.y * size.height)
layer.addSublayer(anchorPointlayer)
}
}
func setAnchorPoint(anchorPoint: CGPoint) {
var newPoint = CGPoint(x: bounds.size.width * anchorPoint.x, y: bounds.size.height * anchorPoint.y)
var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y)
newPoint = newPoint.applying(transform)
oldPoint = oldPoint.applying(transform)
var position = layer.position
position.x -= oldPoint.x
position.x += newPoint.x
position.y -= oldPoint.y
position.y += newPoint.y
layer.position = position
layer.anchorPoint = anchorPoint
}
}
Включение Show Anchor Point отобразит красную точку, чтобы вы лучше видеть, где точка привязки будет визуально. Вы всегда можете отключить его позже.
Это действительно помогло мне при планировании преобразований в UIViews.
У меня была та же проблема. Решение Брэда Ларсона отлично справилось, даже когда вид повернут. Вот его решение, переведенное в код.
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
view.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
view.bounds.size.height * view.layer.anchorPoint.y);
newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);
CGPoint position = view.layer.position;
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}
И быстрый эквивалент:
func setAnchorPoint(anchorPoint: CGPoint, forView view: UIView) {
var newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
var oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)
newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)
var position = view.layer.position
position.x -= oldPoint.x
position.x += newPoint.x
position.y -= oldPoint.y
position.y += newPoint.y
view.layer.position = position
view.layer.anchorPoint = anchorPoint
}
Вот ответ user945711 , настроенный для NSView в OS X. Помимо NSView, не имеющего свойства .center
, кадр NSView не изменяется (вероятно, потому, что NSViews не имеют CALayer по умолчанию ), но исходное состояние CALayer изменяется при изменении привязки.
func setAnchorPoint(anchorPoint: NSPoint, view: NSView) {
guard let layer = view.layer else { return }
let oldOrigin = layer.frame.origin
layer.anchorPoint = anchorPoint
let newOrigin = layer.frame.origin
let transition = NSMakePoint(newOrigin.x - oldOrigin.x, newOrigin.y - oldOrigin.y)
layer.frame.origin = NSMakePoint(layer.frame.origin.x - transition.x, layer.frame.origin.y - transition.y)
}
Существует такое простое решение. Это основано на ответе Кенни. Но вместо применения старого фрейма используйте его начало и новые для вычисления перехода, затем примените этот переход к центру. Он также работает с повернутым видом! Вот код, намного проще, чем другие решения:
- (void) setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view {
CGPoint oldOrigin = view.frame.origin;
view.layer.anchorPoint = anchorPoint;
CGPoint newOrigin = view.frame.origin;
CGPoint transition;
transition.x = newOrigin.x - oldOrigin.x;
transition.y = newOrigin.y - oldOrigin.y;
view.center = CGPointMake (view.center.x - transition.x, view.center.y - transition.y);
}
И версия Swift:
func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
let oldOrigin = view.frame.origin
view.layer.anchorPoint = anchorPoint
let newOrigin = view.frame.origin
let transition = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)
view.center = CGPoint(x: view.center.x - transition.x, y: view.center.y - transition.y)
}
anchorPoint
иposition
вместе? – dumbfingers 6 February 2013 в 14:11